안드로이드 용 트위터 클라이언트 구축에 대한 tutsplus.com 튜토리얼을 사용합니다. 전체 응용 프로그램을 빌드하고 실행하면 다음과 같은 오류가 Eclipse의 logcat에 나타납니다.android.os.NetworkOnMainThreadException - 안드로이드 용 트위터 클라이언트
android.os.NetworkOnMainThreadException - 몇 가지를 읽으면 내가 말하는 API 레벨을 처리해야합니다. 현재 API 4.0 인 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"); }
}
}
스택 오버플로에 오신 것을 환영합니다. 사용자에게 코드를 다시 작성하도록 요청하는 것은 일반적으로 어떻게 시도했는지, 왜 작동하지 않는지에 대한 노력을 보여주지 않으면 발생하지 않습니다. 이 경우, 예, AsyncTask는 원하는 작업이며 API 레벨과 관련이 없으므로 모든 프레임 워크의 주 스레드에서 네트워크 작업을 수행해서는 안됩니다. –
NetworkOnMainThreadException으로 충돌이 발생합니다. Android 4.0 및 targetSDKVersion과 관련이 있다고 가정합니다. 그렇습니다. UI 스레드에서 네트워킹을하는 것은 나쁜 습관입니다. –
@Dirk 어떻게 targetSDKVersion이 너무 높을 수 있습니까? 그들은 항상 하위 호환성이 있거나 호환성 패키지를 사용할 수 있습니다. –