【问题标题】:Android - How to download a file from a webserverAndroid - 如何从网络服务器下载文件
【发布时间】:2013-03-23 10:29:33
【问题描述】:

在我的应用程序中,我正在从网络服务器下载kml 文件。我已经在我的 android 清单文件中设置了外部存储和互联网的权限。我是 Android 新手,非常感谢您的帮助。

MainActivity.java

package com.example.demo;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DownloadFiles();
    }

    public void DownloadFiles(){
        try {
            URL u = new URL("http://www.qwikisoft.com/demo/ashade/20001.kml");
            InputStream is = u.openStream();
            DataInputStream dis = new DataInputStream(is);

            byte[] buffer = new byte[1024];
            int length;

            FileOutputStream fos = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/" + "data/test.kml"));
            while ((length = dis.read(buffer)) > 0) {
                fos.write(buffer, 0, length);
            }

        } catch (MalformedURLException mue) {
            Log.e("SYNC getUpdate", "malformed url error", mue);
        } catch (IOException ioe) {
            Log.e("SYNC getUpdate", "io error", ioe);
        } catch (SecurityException se) {
            Log.e("SYNC getUpdate", "security error", se);
        }
    }
}

Android 清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.demo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity
            android:name="com.example.demo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Logcat 错误:

致命异常:主要 java.lang.RuntimeException:无法启动活动 ComponentInfo{com.example.demo/com.example.demo.MainActivity}:android.os.NetworkOnMainThreadException 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1956) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981) 在 android.app.ActivityThread.access$600(ActivityThread.java:123) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147) 在 android.os.Handler.dispatchMessage(Handler.java:99) 在 android.os.Looper.loop(Looper.java:137) 在 android.app.ActivityThread.main(ActivityThread.java:4424) 在 java.lang.reflect.Method.invokeNative(Native Method) 在 java.lang.reflect.Method.invoke(Method.java:511) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 在 dalvik.system.NativeStart.main(本机方法) 引起:android.os.NetworkOnMainThreadException 在 android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1099) 在 java.net.InetAddress.lookupHostByName(InetAddress.java:391) 在 java.net.InetAddress.getAllByNameImpl(InetAddress.java:242) 在 java.net.InetAddress.getAllByName(InetAddress.java:220) 在 libcore.net.http.HttpConnection.(HttpConnection.java:71) 在 libcore.net.http.HttpConnection.(HttpConnection.java:50) 在 libcore.net.http.HttpConnection$Address.connect(HttpConnection.java:351) 在 libcore.net.http.HttpConnectionPool.get(HttpConnectionPool.java:86) 在 libcore.net.http.HttpConnection.connect(HttpConnection.java:128) 在 libcore.net.http.HttpEngine.openSocketConnection(HttpEngine.java:308) 在 libcore.net.http.HttpEngine.connect(HttpEngine.java:303) 在 libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:282) 在 libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:232) 在 libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:273) 在 libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:168) 在 java.net.URL.openStream(URL.java:462) 在 com.example.demo.MainActivity.DownloadFiles(MainActivity.java:30) 在 com.example.demo.MainActivity.onCreate(MainActivity.java:24) 在 android.app.Activity.performCreate(Activity.java:4465) 在 android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049) 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)

编辑

package com.example.demo;

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;

public class MainActivity extends Activity {

    private ProgressDialog pDialog;
    public static final int progress_bar_type = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new DownloadFileFromURL().execute("http://www.qwikisoft.com/demo/ashade/20001.kml");
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case progress_bar_type: // we set this to 0
                pDialog = new ProgressDialog(this);
                pDialog.setMessage("Downloading file. Please wait...");
                pDialog.setIndeterminate(false);
                pDialog.setMax(100);
                pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                pDialog.setCancelable(true);
                pDialog.show();
                return pDialog;
            default:
                return null;
        }
    }

    class DownloadFileFromURL extends AsyncTask<String, String, String> {

        /**
         * Before starting background thread Show Progress Bar Dialog
         **/
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            showDialog(progress_bar_type);
        }

        /**
         * Downloading file in background thread
         **/
        @Override
        protected String doInBackground(String... f_url) {
            int count;
            try {
                URL url = new URL(f_url[0]);
                URLConnection conection = url.openConnection();
                conection.connect();

                // this will be useful so that you can show a tipical 0-100%
                // progress bar
                int lenghtOfFile = conection.getContentLength();

                // download the file
                InputStream input = new BufferedInputStream(url.openStream(),
                        8192);

                // Output stream
                OutputStream output = new FileOutputStream(Environment
                        .getExternalStorageDirectory().toString()
                        + "/data/downloadedfile.kml");

                byte data[] = new byte[1024];

                long total = 0;

                while ((count = input.read(data)) != -1) {
                    total += count;
                    // publishing the progress....
                    // After this onProgressUpdate will be called
                    publishProgress("" + (int) ((total * 100) / lenghtOfFile));

                    // writing data to file
                    output.write(data, 0, count);
                }

                // flushing output
                output.flush();

                // closing streams
                output.close();
                input.close();
            } catch (Exception e) {
                Log.e("Error: ", e.getMessage());
            }

            return null;
        }

        /**
         * Updating progress bar
         **/
        protected void onProgressUpdate(String... progress) {
            // setting progress percentage
            pDialog.setProgress(Integer.parseInt(progress[0]));
        }

        /**
         * After completing background task Dismiss the progress dialog
         **/
        @Override
        protected void onPostExecute(String file_url) {
            // dismiss the dialog after the file was downloaded
            dismissDialog(progress_bar_type);
        }
    }
}

当我在模拟器中运行此代码时,代码仍然无法运行 - 文件未下载。

【问题讨论】:

  • 不要在主线程上执行网络相关操作。创建一个单独的线程来下载文件。
  • 你的意思是异步任务吗?
  • 你不需要在conection.getContentLength()之前调用conection.connect(),因为它是由conection.getContentLength()隐式调用的

标签: android webserver kml


【解决方案1】:

使用Async task

当你想下载文件时调用:new DownloadFileFromURL().execute(file_url);

public class MainActivity extends Activity {

    // Progress Dialog
    private ProgressDialog pDialog;
    public static final int progress_bar_type = 0;

    // File url to download
    private static String file_url = "http://www.qwikisoft.com/demo/ashade/20001.kml";

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

        setContentView(R.layout.activity_main);

        new DownloadFileFromURL().execute(file_url);

    }

    /**
     * Showing Dialog
     * */

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case progress_bar_type: // we set this to 0
            pDialog = new ProgressDialog(this);
            pDialog.setMessage("Downloading file. Please wait...");
            pDialog.setIndeterminate(false);
            pDialog.setMax(100);
            pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            pDialog.setCancelable(true);
            pDialog.show();
            return pDialog;
        default:
            return null;
        }
    }

    /**
     * Background Async Task to download file
     * */
    class DownloadFileFromURL extends AsyncTask<String, String, String> {

        /**
         * Before starting background thread Show Progress Bar Dialog
         * */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            showDialog(progress_bar_type);
        }

        /**
         * Downloading file in background thread
         * */
        @Override
        protected String doInBackground(String... f_url) {
            int count;
            try {
                URL url = new URL(f_url[0]);
                URLConnection connection = url.openConnection();
                connection.connect();

                // this will be useful so that you can show a tipical 0-100%
                // progress bar
                int lenghtOfFile = connection.getContentLength();

                // download the file
                InputStream input = new BufferedInputStream(url.openStream(),
                        8192);

                // Output stream
                OutputStream output = new FileOutputStream(Environment
                        .getExternalStorageDirectory().toString()
                        + "/2011.kml");

                byte data[] = new byte[1024];

                long total = 0;

                while ((count = input.read(data)) != -1) {
                    total += count;
                    // publishing the progress....
                    // After this onProgressUpdate will be called
                    publishProgress("" + (int) ((total * 100) / lenghtOfFile));

                    // writing data to file
                    output.write(data, 0, count);
                }

                // flushing output
                output.flush();

                // closing streams
                output.close();
                input.close();

            } catch (Exception e) {
                Log.e("Error: ", e.getMessage());
            }

            return null;
        }

        /**
         * Updating progress bar
         * */
        protected void onProgressUpdate(String... progress) {
            // setting progress percentage
            pDialog.setProgress(Integer.parseInt(progress[0]));
        }

        /**
         * After completing background task Dismiss the progress dialog
         * **/
        @Override
        protected void onPostExecute(String file_url) {
            // dismiss the dialog after the file was downloaded
            dismissDialog(progress_bar_type);

        }

    }
}

如果在 4.0 中不工作,则添加:

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

【讨论】:

  • 私有 ProgressDialog pDialog;
  • 嘿,我已经把代码放在上面了..请检查..文件还没有下载...
  • 这个例子有问题。当代码在 getContentLength() 行运行时,将使用 url 发送请求,然后在 url.openStream() 行再次发送。
  • while (!isCancelled() &amp;&amp; (count = input.read(data)) != -1)怎么样
【解决方案2】:

简单的 kotlin 版本

fun download(link: String, path: String) {
    URL(link).openStream().use { input ->
        FileOutputStream(File(path)).use { output ->
            input.copyTo(output)
        }
    }
}

有没有办法通过这种方法获取下载进度或下载大小?

这与InputStream.copyTo 中定义的实现相同,但有进步

/*inline*/ fun download(link: String, path: String, progress: ((Long, Long) -> Unit)? = null): Long {
    val url = URL(link)
    val connection = url.openConnection()
    connection.connect()
    val length = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) connection.contentLengthLong else
        connection.contentLength.toLong()
    url.openStream().use { input ->
        FileOutputStream(File(path)).use { output ->
            val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
            var bytesRead = input.read(buffer)
            var bytesCopied = 0L
            while (bytesRead >= 0) {
                output.write(buffer, 0, bytesRead)
                bytesCopied += bytesRead
                progress?.invoke(bytesCopied, length)
                bytesRead = input.read(buffer)
            }
            return bytesCopied
        }
    }
}

使用示例:

val handler = object : Handler(Looper.getMainLooper()) {

    override fun handleMessage(msg: Message) {
        // length may be negative because it is based on http header
        val (progress, length) = msg.obj as Pair<Long, Long>
    }
}

// call this outside of main thread
val totalSize = download("http://example.site/path/to/file", "path/to/file") { progress, length ->
    // handling the result on main thread
    handler.sendMessage(handler.obtainMessage(0, progress to length))
}

【讨论】:

  • 又一次,kotlin 让应该简单的事情变得简单。谢谢!
  • 感谢您采用这种简单的方法!有没有办法通过这种方法获取下载进度或下载大小?
  • @JordanH 这取决于你如何使用这个功能
【解决方案3】:

我会推荐使用 Android DownloadManager

DownloadManager downloadmanager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse("http://www.example.com/myfile.mp3");

DownloadManager.Request request = new DownloadManager.Request(uri);
request.setTitle("My File");
request.setDescription("Downloading");
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setVisibleInDownloadsUi(false);
request.setDestinationUri(Uri.parse("file://" + folderName + "/myfile.mp3"));

downloadmanager.enqueue(request);

【讨论】:

  • 我发现DownloadManager 在慢速手机上速度非常慢!例如,我最近的调查显示,获取文件大小需要 600 毫秒,然后它闲置了 5400 毫秒,然后在 700 毫秒内下载了一个 2MB 的文件。我建议使用适当的下载管理器,而不是 Google 的。
  • @SomeoneSomewhere 我完全同意,我很久以前就切换到了 fetch2 库(它非常快速和可靠),但 DownloadManager 是我可以给出的更标准和长期的答案。
【解决方案4】:

使用下载管理器下载文件

    DownloadManager downloadmanager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
    Uri uri = Uri.parse("https://www.globalgreyebooks.com/content/books/ebooks/game-of-life.pdf");

    DownloadManager.Request request = new DownloadManager.Request(uri);
    request.setTitle("My File");
    request.setDescription("Downloading");//request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,"game-of-life");
    downloadmanager.enqueue(request);

【讨论】:

  • 这行得通。但它复制了公共空间中的文件。我们可以将它复制到应用程序内部存储器中,如资产。谢谢
  • 简而言之:构建 apk 后,您无法在“原始”或“资产”文件夹中进行更改。
  • 谢谢。我正在使用 API 30。如何在公共空间创建一个目录,然后在那里下载文件。谢谢
  • 您可以使用Scope Storage
【解决方案5】:

在主线程上执行网络操作是不好的做法,这就是您看到NetworkOnMainThreadException 的原因。它被政策所阻止。如果您确实必须这样做以进行测试,请将以下内容放入您的 OnCreate:

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

请记住,这样做是非常糟糕的做法,最好将您的网络代码移动到AsyncTask 或Thread。

【讨论】:

    【解决方案6】:

    Mr.Iam4fun 你的代码答案在这里..你将使用线程...

       findViewById(R.id.download).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               
                new Thread(new Runnable() {
                    public void run() {
                        DownloadFiles();
                    }
                }).start();
    

    然后..

     public void DownloadFiles(){
    
            try {
                URL u = new URL("http://www.qwikisoft.com/demo/ashade/20001.kml");
                InputStream is = u.openStream();
    
                DataInputStream dis = new DataInputStream(is);
    
                byte[] buffer = new byte[1024];
                int length;
    
                FileOutputStream fos = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/" + "data/test.kml"));
                while ((length = dis.read(buffer))>0) {
                  fos.write(buffer, 0, length);
                }
    
              } catch (MalformedURLException mue) {
                Log.e("SYNC getUpdate", "malformed url error", mue);
              } catch (IOException ioe) {
                Log.e("SYNC getUpdate", "io error", ioe);
              } catch (SecurityException se) {
                Log.e("SYNC getUpdate", "security error", se);
              }
    }
    }
    

    【讨论】:

      【解决方案7】:

      您应该使用 AsyncTask(或其他方式在后台执行网络操作)。

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
      
          //create and execute the download task
          MyAsyncTask async = new MyAsyncTask();
          async.execute();
      
      }
      
      private class MyAsyncTask extends AsyncTask<Void, Void, Void>{
      
          //execute on background (out of the UI thread)
          protected Long doInBackground(URL... urls) {
              DownloadFiles();
          }
      
      }
      

      更多关于 AsyncTask 的信息在Android documentation

      希望对你有帮助。

      【讨论】:

        【解决方案8】:

        在线程或 AsyncTask 中运行这些代码。为了避免重复调用相同的_url(一次getContentLength(),一次openStream()),请使用Apache的IOUtils.toByteArray。

        void downloadFile(String _url, String _name) {
            try {
                URL u = new URL(_url);
                DataInputStream stream = new DataInputStream(u.openStream());
                byte[] buffer = IOUtils.toByteArray(stream);
                FileOutputStream fos = mContext.openFileOutput(_name, Context.MODE_PRIVATE);
                fos.write(buffer);
                fos.flush();
                fos.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return;
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
        }
        

        【讨论】:

          【解决方案9】:

          使用AsyncTask 和

          把你的下载文件代码放到doinbackground里面..

          android 不允许再在主线程上执行繁重的任务以避免 ANR(应用程序无响应)错误

          【讨论】:

            【解决方案10】:

            这是帮助您从服务器下载文件的代码,同时您可以在状态栏上看到下载进度。

            查看我的代码下图中的功能:

            STEP - 1 : 在 DownloadFileFromURL.java 类文件上创建以从服务器下载文件内容。这里我创建了一个异步任务来下载文件。

            public class DownloadFileFromURL extends AsyncTask<String, Integer, String> {
            
            private NotificationManager mNotifyManager;
            private NotificationCompat.Builder build;
            private File fileurl;
            int id = 123;
            OutputStream output;
            private Context context;
            private String selectedDate;
            private String ts = "";
            
            public DownloadFileFromURL(Context context, String selectedDate) {
                this.context = context;
                this.selectedDate = selectedDate;
            
            }
            
            protected void onPreExecute() {
                super.onPreExecute();
            
                mNotifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
                build = new NotificationCompat.Builder(context);
                build.setContentTitle("Download")
                        .setContentText("Download in progress")
                        .setChannelId(id + "")
                        .setAutoCancel(false)
                        .setDefaults(0)
                        .setSmallIcon(R.drawable.ic_menu_download);
            
                // Since android Oreo notification channel is needed.
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    NotificationChannel channel = new NotificationChannel(id + "",
                            "Social Media Downloader",
                            NotificationManager.IMPORTANCE_HIGH);
                    channel.setDescription("no sound");
                    channel.setSound(null, null);
                    channel.enableLights(false);
                    channel.setLightColor(Color.BLUE);
                    channel.enableVibration(false);
                    mNotifyManager.createNotificationChannel(channel);
            
                }
                build.setProgress(100, 0, false);
                mNotifyManager.notify(id, build.build());
                String msg = "Download started";
                //CustomToast.showToast(context,msg);
            }
            
            @Override
            protected String doInBackground(String... f_url) {
                int count;
                ts = selectedDate.split("T")[0];
                try {
                    URL url = new URL(f_url[0]);
                    URLConnection conection = url.openConnection();
                    conection.connect();
                    int lenghtOfFile = conection.getContentLength();
            
                    InputStream input = new BufferedInputStream(url.openStream(),
                            8192);
                    // Output stream
                    output = new FileOutputStream(Environment
                            .getExternalStorageDirectory().toString()
                            + Const.DownloadPath + ts + ".pdf");
                    fileurl = new File(Environment.getExternalStorageDirectory()
                            + Const.DownloadPath + ts + ".pdf");
                    byte[] data = new byte[1024];
            
                    long total = 0;
            
                    while ((count = input.read(data)) != -1) {
                        total += count;
                        int cur = (int) ((total * 100) / lenghtOfFile);
            
                        publishProgress(Math.min(cur, 100));
                        if (Math.min(cur, 100) > 98) {
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                Log.d("Failure", "sleeping failure");
                            }
                        }
                        Log.i("currentProgress", "currentProgress: " + Math.min(cur, 100) + "\n " + cur);
            
                        output.write(data, 0, count);
                    }
            
                    output.flush();
            
                    output.close();
                    input.close();
            
                } catch (Exception e) {
                    Log.e("Error: ", e.getMessage());
                }
            
                return null;
            }
            
            protected void onProgressUpdate(Integer... progress) {
                build.setProgress(100, progress[0], false);
                mNotifyManager.notify(id, build.build());
                super.onProgressUpdate(progress);
            }
            
            @Override
            protected void onPostExecute(String file_url) {
                build.setContentText("Download complete");
                build.setProgress(0, 0, false);
                mNotifyManager.notify(id, build.build());
            } }
            

            注意:如果你想要带有导入包的代码,那么Click Here

            现在第 2 步:您需要在点击事件上调用上述异步任务。例如,我设置了 pdf 图像图标。 要调用 AsyncTask,请使用以下代码:

             new DownloadFileFromURL(fContext,filename).execute(serverFileUrl);
            

            注意:这里可以看到文件参数中的filename变量。这是我用来将下载的文件保存在本地设备中的名称。目前我只下载 pdf 文件,但您可以在 serverFileUrl 参数中使用您的 url。

            【讨论】:

            • 这看起来像 DIY 版的下载管理器..
            【解决方案11】:

            当您点击下载按钮时,首先您必须创建要保存它的本地存储路径,我们应该将下载文件功能放入 ExecutorService(用于代替 AsyncTask,因为不推荐使用 AsyncTask)。

            // Create bamchik folder if not created yet
            val folder = File(context?.getExternalFilesDir(null)?.absoluteFile, "MusicTrack")
            val check = folder.mkdirs()
            Log.v(mTAG, "check: $check")
            var path = context?.getExternalFilesDir(null)?.absolutePath + "/MusicTrack/music." + fileExt
            val SDK_INT = Build.VERSION.SDK_INT
            if (SDK_INT > 8) {
                val policy: StrictMode.ThreadPolicy = StrictMode.ThreadPolicy.Builder().permitAll().build()
                StrictMode.setThreadPolicy(policy)
                val executor: ExecutorService = Executors.newSingleThreadExecutor()
                val handler = object : Handler(Looper.getMainLooper()) {
                    override fun handleMessage(msg: Message) {
                        // Length is the total size of file and progress is how much file have been downloaded, now you can show progress according to this
                        val (progress, length) = msg.obj as Pair<Long, Long> 
                        Log.v(mTAG, "Progress: $progress, Length: $length")
                    }
            
                }
            
                executor.execute {
                    //Background work here
                    download(link, path) { progress, length ->
                        // handling the result on main thread
                        handler.sendMessage(handler.obtainMessage(0, progress to length))
                    }
                    handler.post {
                        //UI Thread work here
                    }
                }
            }
            

            从url下载文件的功能

            fun download(link: String, path: String, progress: ((Long, Long) -> Unit)? = null): Long {
                val url = URL(link)
                val connection = url.openConnection()
                connection.connect()
                val length = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) connection.contentLengthLong else connection.contentLength.toLong()
                url.openStream().use { input ->
                    FileOutputStream(File(path)).use { output ->
                        val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
                        var bytesRead = input.read(buffer)
                        var bytesCopied = 0L
                        while (bytesRead >= 0) {
                            output.write(buffer, 0, bytesRead)
                            bytesCopied += bytesRead
                            progress?.invoke(bytesCopied, length)
                            bytesRead = input.read(buffer)
                        }
            
                        return bytesCopied
                    }
                }
            }
            

            【讨论】:

              【解决方案12】:

              除了使用AsyncTask,你还可以把操作放在runnable-

              Runnable r=new Runnable()
              {
              
              public void run()
              {
              ///-------network operation code
              }
              };
              
              //--------call r in this way--
              Thread t=new Thread(r);`enter code here`
              t.start();
              
              Also put the UI work in a handler..such as updating a textview etc..
              

              【讨论】:

                【解决方案13】:

                这就是我使用改造下载、保存文件并将文件返回到视图模型的方式:)

                界面文件下载器 { 伴随对象{ 私有 val 实例:懒惰的 FileDownloader {

                        val loggingInterceptor = HttpLoggingInterceptor()
                        loggingInterceptor.level = HttpLoggingInterceptor.Level.HEADERS
                
                        val client = OkHttpClient.Builder()
                            .connectTimeout(120, TimeUnit.SECONDS)
                            .readTimeout(300, TimeUnit.SECONDS)
                            .callTimeout(300, TimeUnit.SECONDS)
                            .addInterceptor(loggingInterceptor)
                            .build()
                
                        val downloaderApi = Retrofit.Builder()
                            .baseUrl("https://exmaple.com")
                            .addConverterFactory(GsonConverterFactory.create())
                            .client(client)
                            .build()
                            .create(FileDownloader::class.java)
                
                        downloaderApi
                    }
                
                    suspend fun downloadAndSaveFile(
                        url: String
                    ): File {
                        val file = getPdfFile()
                        val responseBody = try {
                            instance.downloadFile(url)
                        } catch (e: Exception) {
                            throw FileDownloadException(url, e)
                        }
                        @Suppress("BlockingMethodInNonBlockingContext")
                        try {
                            FileOutputStream(file).use { outputStream ->
                                responseBody.byteStream().use { inputStream ->
                                    inputStream.copyTo(outputStream)
                                }
                            }
                        } catch (e: Exception) {
                            throw FileDownloadException(url, e)
                        }
                        return file
                    }
                
                
                }
                
                @GET
                @Streaming
                suspend fun downloadFile(@Url utl: String): ResponseBody
                }
                
                fun getPdfFile(): File {
                    File(App.appContext.filesDir, "pdfs").apply { mkdirs() }
                    return File(App.appContext.filesDir, "tempPDF.pdf")
                }
                
                class FileDownloadException(url: String, e: Exception) : Throwable()
                

                【讨论】:

                  【解决方案14】:

                  从 api 级别 11 或 Honeycomb 开始,禁止在主线程上进行网络操作。使用线程或异步任务。欲了解更多信息,请访问https://developer.android.com/reference/android/os/NetworkOnMainThreadException.html

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多