2012-05-20 5 views
2

안드로이드 용 트위터 클라이언트 구축에 대한 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"); } 
} 

}

+1

스택 오버플로에 오신 것을 환영합니다. 사용자에게 코드를 다시 작성하도록 요청하는 것은 일반적으로 어떻게 시도했는지, 왜 작동하지 않는지에 대한 노력을 보여주지 않으면 발생하지 않습니다. 이 경우, 예, AsyncTask는 원하는 작업이며 API 레벨과 관련이 없으므로 모든 프레임 워크의 주 스레드에서 네트워크 작업을 수행해서는 안됩니다. –

+0

NetworkOnMainThreadException으로 충돌이 발생합니다. Android 4.0 및 targetSDKVersion과 관련이 있다고 가정합니다. 그렇습니다. UI 스레드에서 네트워킹을하는 것은 나쁜 습관입니다. –

+0

@Dirk 어떻게 targetSDKVersion이 너무 높을 수 있습니까? 그들은 항상 하위 호환성이 있거나 호환성 패키지를 사용할 수 있습니다. –

답변

3

당신은 https://developer.android.com/reference/android/os/AsyncTask.html이 제공하는 기능 외에 무엇이 필요합니까? AsyncTask 사용과 관련하여 특정 질문이 있습니까?

당신은 문제의 라인을 가져 와서 doInBackground() 안에 넣습니다. 결과가있는 경우 UI를 조작하지 않으면 결과 처리 코드를 doInBackground()에 넣습니다. UI를 변경하면 onPostExecute()에 넣습니다.

그런 다음 당신이 예외를 던져 줄을 바꿉니다.

새로운 MyTask() (PARAM)을 실행;

이 코드는 이후에 즉시 실행됩니다.

+0

, 당신의 제안은됩니다의 : \t에서 android.os.AsyncTask $ 3.done (AsyncTask.java:299) java.util.concurrent.FutureTask.finishCompletion (FutureTask.java:352)에서 을에서 java.util.concurrent.FutureTask.setException (FutureTask.java:219) 에서 java.util.concurrent.FutureTask.run (FutureTask.java:239) –

+0

다시 말해주십시오. 나는 당신이 예외를 얻었지만 당신의 불완전한 stacktrace에서 그것을 볼 수 없다고 생각한다. –

+0

내가 말하고자하는 것은 NetworkOnMainUiThread와 관련이있는 Twitter4j 오류가 AsyncTask로 쉽게 해결되지 않는다는 것입니다. onPostExecute 메소드뿐만 아니라 많은 asynctasks가 잠재적으로있을 수 있습니다. Twitter4j로 문제를 해결 했습니까? –

0

질문을 기다릴 필요가 없습니다. 현재 대부분의 문제는 AsyncTask 사용을 통해 해결할 수 있습니다.이 작업은 Here을 통해 잘 문서화되어 있습니다. 자습서를 앱/프로젝트의 프레임 워크로 사용하지 말고 직접 프로젝트를 설정하는 방법을 안내하는 가이드로 사용하는 것이 좋습니다. 자신의 AsyncTask를 생성하고이를 사용하여 메인 태스크에서 네트워크 관련 데이터를 얻거나 AsynTasks 외의 대부분의 태스크가 던져진 예외로 인해 대개 싫어하는 경우 네트워크에서 데이터를 가져 오는 것이 좋습니다.

관련 문제