2016-07-21 4 views
3

Ich versuche, Retrofit-Bibliothek zu verwenden, um eine Datei (apk: 2mB) von URL in einem getrennten Thread herunterzuladen.Android: NetworkOnMainThreadException tritt auf, wenn Retrofit in einem neuen Thread verwendet wird?

mein Code Hier ist (siehe Tutorial: https://futurestud.io/blog/retrofit-2-how-to-download-files-from-server):

public class MainActivity extends AppCompatActivity { 
    String fileUrl = "....."; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       FileDownloadService downloadService = ServiceGenerator.createService(FileDownloadService.class); 
       Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl2); 

       call.enqueue(new Callback<ResponseBody>() { 
        @Override 
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { 
         if (response.isSuccessful()) { 
          writeResponseBodyToDisk(response.body()); 
         } else { 
          Log.d("fail", "server contact failed"); 
         } 
        } 

        @Override 
        public void onFailure(Call<ResponseBody> call, Throwable t) { 
         Log.e("fail", "error"); 
        } 
       }); 
      } 
     }).start(); 
    } 

    private boolean writeResponseBodyToDisk(ResponseBody body) { 
     try { 
      File apkFile = new File(getExternalFilesDir(null) + 
        File.separator + "test.apk"); 
      InputStream inputStream = null; 
      OutputStream outputStream = null; 
      try { 
       byte[] fileReader = new byte[4096]; 

       inputStream = body.byteStream(); 
       outputStream = new FileOutputStream(apkFile); 

       while (true) { 
        int read = inputStream.read(fileReader); 

        if (read == -1) { 
         break; 
        } 

        outputStream.write(fileReader, 0, read); 
       } 

       outputStream.flush(); 

       return true; 
      } catch (IOException e) { 
       e.printStackTrace(); 
       return false; 
      } finally { 
       if (inputStream != null) { 
        inputStream.close(); 
       } 

       if (outputStream != null) { 
        outputStream.close(); 
       } 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
      return false; 
     } 

    } 
} 

Der Code meines Dienstes:

public class ServiceGenerator { 

    public static final String API_BASE_URL = "https://your.api.url/"; 

    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); 

    private static Retrofit.Builder builder = 
     new Retrofit.Builder() 
       .baseUrl(API_BASE_URL) 
       .addConverterFactory(GsonConverterFactory.create()); 

    public static <S> S createService(Class<S> serviceClass) { 
     Retrofit retrofit = builder.client(httpClient.build()).build(); 
     return retrofit.create(serviceClass); 
    } 
} 

public interface FileDownloadService { 
    @Streaming 
    @GET 
    Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl); 
} 

ich das Herunterladen Aufgabe in einem neuen Thread gestartet, aber ich habe diese:

android.os.NetworkOnMainThreadException 

Wenn ich ein Bild von 3kB herunterladen, funktioniert das Programm gut. Wenn ich Folgendes hinzufügen Code:

StrictMode policy violation; ~duration=1342 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2 

Hier ist der Stacktrace:

Process: com.testapp.yiyuanguo.apkinstall, PID: 25788 
android.os.NetworkOnMainThreadException 
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147) 
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:249) 
at libcore.io.IoBridge.recvfrom(IoBridge.java:553) 
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:485) 
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:37) 
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:237) 
at okio.Okio$2.read(Okio.java:139) 
at okio.AsyncTimeout$2.read(AsyncTimeout.java:211) 
at okio.RealBufferedSource.read(RealBufferedSource.java:50) 
at okhttp3.internal.http.Http1xStream$FixedLengthSource.read(Http1xStream.java:381) 
at okhttp3.internal.Util.skipAll(Util.java:178) 
at okhttp3.internal.Util.discard(Util.java:160) 
at okhttp3.internal.http.Http1xStream$FixedLengthSource.close(Http1xStream.java:397) 
at okio.RealBufferedSource.close(RealBufferedSource.java:396) 
at okio.ForwardingSource.close(ForwardingSource.java:43) 
at okio.RealBufferedSource.close(RealBufferedSource.java:396) 
at okio.RealBufferedSource$1.close(RealBufferedSource.java:384) 
at com.testapp.apkinstall.MainActivity.writeResponseBodyToDisk(MainActivity.java:87) 
at com.testapp.apkinstall.MainActivity.access$000(MainActivity.java:24) 
at com.testapp.apkinstall.MainActivity$1$1.onResponse(MainActivity.java:42) 
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:68) 
at android.os.Handler.handleCallback(Handler.java:739) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:135) 
at android.app.ActivityThread.main(ActivityThread.java:5254) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693) 
+1

Post den gesamten StackTrace. Entfernen Sie die 'StrictMode.ThreadPolicy' – Blackbelt

+2

Sie erkennen, dass' execute() 'den Netzwerkaufruf synchron ausführt, aber' enqueue() 'ist ** bereits asynchron ** und Sie müssen *** nicht starten neuer Thread() ***, oder? – EpicPandaForce

+0

@Blackbelt siehe mein update, danke! – programforjoy

Antwort

3

onResponse laufen auf den UI Thread und byteStream() Zugriffe

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); 
    StrictMode.setThreadPolicy(policy); 

Das Herunterladen für eine Weile und der Stop mit den Informationen verfahren wird die unterliegende Netzwerkverbindung und, wie die Ausnahme angibt, können Sie es nicht tun. Wie @EpicPandaForce weist darauf hin, da Sie Ihre eigenen Thread verwenden, müssen Sie nicht enqueue nennen, aber Sie können direkt execute() aufrufen, die ein blockierenden Aufruf ist die Response<T> zurückgibt:

Response<ResponseBody> response = call.execute(); 
if (response.isSuccessful()) { 
    writeResponseBodyToDisk(response.body()); 
} 

in diesem Fall alles läuft im Kontext des Threads und nicht im UI-Thread. Von Ihrem Thread aus können Sie auf byteStream() zugreifen, und Sie werden nicht die NetworkOnMainThreadException

+1

große Antwort! Problem gelöst. – programforjoy