2

Android用のTwitterクライアントの構築に関するtutsplus.comのチュートリアルを使用しています。アプリケーション全体をビルドしました。実行すると、Eclipse の logcat に次のエラーが表示されます。

android.os.NetworkOnMainThreadException - いくつかのことを読んで、私が呼び出している API レベルを処理する必要があります。現在、API 15 である 4.0.3 用にビルドしています。API 11 以降では、UI と同じスレッドでネットワーク呼び出しを行うことはできません。これの背後にある理由は、UI を停止またはクラッシュさせることではありません。ネットワーク呼び出しは、AsyncTask または Service 内にある必要があります。

私の質問/問題/問題の大小は、チュートリアルの作成者が問題を修正するのに役立たないということです。それが私がここにいる理由です。誰かがネットワーク部分を AsyncTask または Service に移動するのを手伝ってくれることを期待して、以下のコードを投稿しています。

package com.jasonsdesign.tweetxy;

import twitter4j.ProfileImage;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;

public class TweetxyActivity extends Activity implements OnClickListener {

/**developer account key for this app*/
public final static String TWIT_KEY = "";
/**developer secret for the app*/
public final static String TWIT_SECRET = "";
/**app url*/
public final static String TWIT_URL = "tweetxy-android:///";

private String LOG_TAG = "TweetxyActivity";

/**Twitter instance*/
private Twitter tweetxyTwitter;
/**request token for accessing user account*/
private RequestToken tweetxyRequestToken;
/**shared preferences to store user details*/
private SharedPreferences tweetxyPrefs;
/**main view for the home timeline*/
private ListView homeTimeline;
/**database helper for update data*/
private TweetxyDataHelper timelineHelper;
/**update database*/
private SQLiteDatabase timelineDB;
/**cursor for handling data*/
private Cursor timelineCursor;
/**adapter for mapping data*/
private UpdateAdapter timelineAdapter;
/**broadcast receiver for when new updates are available*/
private BroadcastReceiver tweetxyStatusReceiver;

//set the profile image display
ProfileImage.ImageSize imageSize = ProfileImage.NORMAL;

/*
 * onCreate behaves differently on first run and subsequent runs
 * - if first run take to Twitter sign in page to grant the app permission
 * - subsequent runs fetch and present the user home timeline
 */
@Override
public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    //get the preferences
    tweetxyPrefs = getSharedPreferences("TweetxyPrefs", 0);
    //find out if the user preferences are set
    if(tweetxyPrefs.getString("user_token", null)==null) {
            //no user preferences so prompt to sign in
        setContentView(R.layout.main);
            //get a twitter instance for authentication
        tweetxyTwitter = new TwitterFactory().getInstance();
            //pass developer key and secret
        tweetxyTwitter.setOAuthConsumer(TWIT_KEY, TWIT_SECRET);
            //try to get request token
        try 
        {
                //get authentication request token
            tweetxyRequestToken = tweetxyTwitter.getOAuthRequestToken(TWIT_URL);
        }
        catch(TwitterException te) { Log.e(LOG_TAG, "TE "+te.getMessage()); }
        //setup button for click listener
        Button signIn = (Button)findViewById(R.id.signin);
        signIn.setOnClickListener(this);
    }
    else 
    {
        //user preferences are set - get timeline
        setupTimeline();
    }

}

/**
 * Click listener handles sign in and tweet button presses
 */
public void onClick(View v) {
    //find view
    switch(v.getId()) {
        //sign in button pressed
    case R.id.signin:
            //take user to twitter authentication web page to allow app access to their twitter account
        String authURL = tweetxyRequestToken.getAuthenticationURL();
        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authURL)));
        break;
        //user has pressed tweet button
    case R.id.tweetbtn:
            //launch tweet activity
        startActivity(new Intent(this, TweetxyTweet.class));
        break;
    default:
        break;
    }
}

/*
 * onNewIntent fires when user returns from Twitter authentication Web page
 */
@Override
protected void onNewIntent(Intent intent) {

    super.onNewIntent(intent);
    //get the retrieved data
    Uri twitURI = intent.getData();
    //make sure the url is correct
    if(twitURI!=null && twitURI.toString().startsWith(TWIT_URL)) 
    {
        //is verification - get the returned data
        String oaVerifier = twitURI.getQueryParameter("oauth_verifier");
        //attempt to retrieve access token
        try
        {
                //try to get an access token using the returned data from the verification page
            AccessToken accToken = tweetxyTwitter.getOAuthAccessToken(tweetxyRequestToken, oaVerifier);
                //add the token and secret to shared prefs for future reference
            tweetxyPrefs.edit()
                .putString("user_token", accToken.getToken())
                .putString("user_secret", accToken.getTokenSecret())
                .commit();
                //display the timeline
            setupTimeline();

        }
        catch (TwitterException te)
        { Log.e(LOG_TAG, "Failed to get access token: "+te.getMessage()); }
    }
}

/**
 * setupTimeline displays the user's main home Twitter timeline
 */

private void setupTimeline() {

    //set the layout
    setContentView(R.layout.timeline);
        //setup onclick listener for tweet button
    LinearLayout tweetClicker = (LinearLayout)findViewById(R.id.tweetbtn);
    tweetClicker.setOnClickListener(this);
        //retrieve the timeline
    try 
    {
            //get reference to the list view
        homeTimeline = (ListView)findViewById(R.id.homeList);
            //instantiate database helper
        timelineHelper = new TweetxyDataHelper(this);
            //get the database
        timelineDB = timelineHelper.getReadableDatabase();

        //query the database, most recent tweets first
        timelineCursor = timelineDB.query("home", null, null, null, null, null, "update_time DESC");
        //manage the updates using a cursor
        startManagingCursor(timelineCursor);
        //instantiate adapter
        timelineAdapter = new UpdateAdapter(this, timelineCursor);
        //apply the adapter to the timeline view
        //this will make it populate the new update data in the view
        homeTimeline.setAdapter(timelineAdapter);
        //instantiate receiver class for finding out when new updates are available
        tweetxyStatusReceiver = new TwitterUpdateReceiver();
        //register for updates
        registerReceiver(tweetxyStatusReceiver, new IntentFilter("TWITTER_UPDATES"));

        //start the service for updates now
        this.getApplicationContext().startService(new Intent(this.getApplicationContext(), TimelineService.class));
    }
    catch(Exception te) { Log.e(LOG_TAG, "Failed to fetch timeline: "+te.getMessage()); }
}

/**
 * Class to implement broadcast receipt for new updates
 */
class TwitterUpdateReceiver extends BroadcastReceiver 
{
    /**
     * When new updates are available, a broadcast is received here
     */

    @Override
    public void onReceive(Context context, Intent intent) {
        //delete db rows
        int rowLimit = 100;
        if(DatabaseUtils.queryNumEntries(timelineDB, "home")>rowLimit) {
            String deleteQuery = "DELETE FROM home WHERE "+BaseColumns._ID+" NOT IN " +
                    "(SELECT "+BaseColumns._ID+" FROM home ORDER BY "+"update_time DESC " +
                            "limit "+rowLimit+")";  
            timelineDB.execSQL(deleteQuery);
        }       

        timelineCursor = timelineDB.query("home", null, null, null, null, null, "update_time DESC");
        startManagingCursor(timelineCursor);
        timelineAdapter = new UpdateAdapter(context, timelineCursor);
        homeTimeline.setAdapter(timelineAdapter);
    }
}

/*
 * When the class is destroyed, close database and service classes
 */
@Override
public void onDestroy() {
    super.onDestroy();
    try 
    {
        //stop the updater service
        stopService(new Intent(this, TimelineService.class));
        //remove receiver register
        unregisterReceiver(tweetxyStatusReceiver);
        //close the database
        timelineDB.close();
    }
    catch(Exception se) { Log.e(LOG_TAG, "unable to stop service or receiver"); }
}

}

4

2 に答える 2

3

https://developer.android.com/reference/android/os/AsyncTask.htmlが提供するもの以外に何が必要ですか? AsyncTask の使用に関して具体的な質問はありますか?

問題のある行を取り、それを doInBackground() 内に配置します。結果がある場合、UI を操作しない場合は、結果処理コードを doInBackground() に配置します。UI に変更を加える場合は、onPostExecute() に入れます。

次に、例外をスローした行を次のように置き換えます。

new MyTask().execute(param);

この後のコードはすぐに実行されることに注意してください。

于 2012-05-20T20:07:04.120 に答える
0

あなたの質問を保留にする必要はないと思います。現在直面している問題のほとんどは、 AsyncTask を使用することで解決できます。これは、Here で十分に文書化されています。チュートリアルをアプリ/プロジェクトのフレームワークとして使用するのではなく、独自のプロジェクトのセットアップ方法を知るためのガイドとして使用することをお勧めします。独自の AsyncTask を作成し、それを使用してネットワークからデータを取得すると、メイン タスクまたは AsynTasks 以外のほとんどのタスクでネットワーク関連のデータを取得することは、例外がスローされるために通常嫌われます。

于 2014-07-11T04:37:47.337 に答える