Android Downloadprovider Source code detailed _android

Source: Internet
Author: User

Android Downloadprovider Source Analysis:

Download source code compiled into two parts, one is downloadprovider.apk, one is downloadproviderui.apk.

These two apk source codes are located in the

Packages/providers/downloadprovider/ui/src
Packages/providers/downloadprovider/src

Among them, the downloadprovider part is the realization of the download logic, and the Downloadproviderui is the realization of the interface part.

Then downloadprovider inside the download although mainly through the operation of Downloadservice, but due to the notification update, download the progress of the display, download management and so on.

So there are still a lot of other classes to operate separately.

Downloadprovider--The encapsulation of database operation, inherits from ContentProvider;
Downloadmanager--Most of the logic is to further encapsulate data operations for external invocation;
Downloadservice--Encapsulation of file Download,delete and other operations, and the operation of the download Norification, inherited from the service;
Downloadnotifier--Status bar notification logic;
Downloadreceiver--cooperate with downloadnotifier in the operation of documents and its notification;
Downloadlist--Download app main interface, file interface interaction;

Download is generally from the browser inside click on the link to start, we first look at the code in browser

In the Src/com/android/browser/downloadhandler.java function of browser, we can see a very complete download call, which we can also refer to when we write our own app:

public static void Startingdownload (activity activity, string URL, String useragent, String contentdisposition, String mimetype, String Referer, Boolean privatebrowsing, long contentlength, string filename, string downloadpath) {//Java.net.URI is a lot stricter than kurl so we have to encode some//extra characters. 
  Fix for b 2538060 and b 1634719 webaddress webaddress; 
    try {webaddress = new webaddress (URL); 
  Webaddress.setpath (Encodepath (Webaddress.getpath ())); The catch (Exception e) {//This is happens for very bad URL, we want to chatch the//Exception here Log 
    . E (LogTag, "Exception trying to parse URL:" + URL); 
  Return 
  String addressstring = webaddress.tostring (); 
  Uri uri = Uri.parse (addressstring); 
  Final Downloadmanager.request Request; 
  try {request = new downloadmanager.request (URI); catch (IllegalArgumentException e) {toast.maketext (activity, R.string.cannot_download, Toast.lenGth_short). Show (); 
  Return 
  } request.setmimetype (MimeType); 
  Set downloaded file destination to/sdcard/download. 
  Or, should it is set to one of the several environment.directory* dirs//depending on mimetype? 
  try {setdestinationdir (downloadpath, filename, request); 
    catch (Exception e) {shownoenoughmemorydialog (activity); 
  Return 
  }//Let this downloaded file is scanned by mediascanner-so the It can//show up into Gallery app for example. 
  Request.allowscanningbymediascanner (); 
  Request.setdescription (Webaddress.gethost ()); 
  Xxx:have to use the ' old ' url since the cookie were stored using the '//old percent-encoded URL. 
  String cookies = Cookiemanager.getinstance (). GetCookie (URL, privatebrowsing); 
  Request.addrequestheader ("cookies", cookies); 
  Request.addrequestheader ("User-agent", useragent); 
  Request.addrequestheader ("Referer", Referer); Request.setnotificationvisibility (downloadmanager.request.visibility_visible_notify_completed); 
  Final Downloadmanager manager = (downloadmanager) activity. Getsystemservice (Context.download_service); 
    New Thread ("Browser download") {public void run () {manager.enqueue (request); 
  }}.start (); 
Showstartdownloadtoast (activity); 

 }

In this operation, we see the various parameters that have been added to the request, and then the last call to the Downloadmanager enqueue for download, and after the start, pops up the toast that started the download. Manager is an example of Downloadmanager, Downloadmanager is present and Frameworks/base/core/java/android/app/downloadmanager.java. You can see that the implementation of Enqueue is:

Public long Enqueue (Request request) { 
  Contentvalues values = request.tocontentvalues (mpackagename); 
  Uri Downloaduri = Mresolver.insert (Downloads.Impl.CONTENT_URI, values); 
  Long id = long.parselong (downloaduri.getlastpathsegment ()); 
  return ID; 

The Enqueue function essentially decomposes the rquest instance into a Contentvalues instance and adds it to the database, and the function returns the ID returned by the inserted data The Contentresolver.insert function is called to the Insert function of the ContentProvider implemented by Downloadprovider, and if we look at the code of the INSERT, we can see that there are many operations. But we just need to focus on a few key parts:

...... 
Insert the relevant request parameters, configuration, etc. into the downloads database; 
long RowID = Db.insert (db_table, NULL, filteredvalues); 
...... 
Insert the relevant request parameters, configuration, etc. into the request_headers database; 
insertrequestheaders (db, RowID, values); 
...... 
if (Values.getasinteger (Downloads.Impl.COLUMN_DESTINATION) = = 
        Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER _download) { 
      //When notification be requested, kick off service to process all 
      //relevant downloads. 
Start Downloadservice for download and other work 
      if (Downloads.Impl.isNotificationToBeDisplayed (Vis)) { 
        Context.startservice (New Intent (context, Downloadservice.class)); 
      } 
    else { 
      Context.startservice (new Intent (context, Downloadservice.class)); 
    } 
    Notifycontentchanged (URI, match); 
    Return Contenturis.withappendedid (Downloads.Impl.CONTENT_URI, RowID); 

On this side, we can see the downloadservice call of the download. Because is a startservice method, so we downloadservice inside, is to go OnCreate method.

@Override public 
void OnCreate () { 
  super.oncreate (); 
  if (CONSTANTS.LOGVV) { 
    log.v (Constants.tag, Service onCreate); 
  } 
 
  if (Msystemfacade = = null) { 
    Msystemfacade = new Realsystemfacade (this); 
  } 
 
  Malarmmanager = (Alarmmanager) getsystemservice (context.alarm_service); 
  Mstoragemanager = new StorageManager (this); 
 
  Mupdatethread = new Handlerthread (TAG + "-updatethread"); 
  Mupdatethread.start (); 
  Mupdatehandler = new Handler (Mupdatethread.getlooper (), mupdatecallback); 
  Mscanner = new Downloadscanner (this); 
  Mnotifier = new Downloadnotifier (this); 
  Mnotifier.cancelall (); 
 
  Mobserver = new Downloadmanagercontentobserver (); 
  Getcontentresolver (). Registercontentobserver (Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, 
      True, mobserver); 
 

This way, we can see that we're going to start a handler to receive callback processing.

Mupdatethread = new Handlerthread (TAG + "-updatethread"); 
 Mupdatethread.start (); 
 Mupdatehandler = new Handler (Mupdatethread.getlooper (), mupdatecallback); 

Then go

Getcontentresolver (). Registercontentobserver (Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, 
        true, Mobserver) 

is to register to monitor the Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI observer.
After OnCreate, the Onstartcommand method is invoked.

@Override 
ublic int Onstartcommand (Intent Intent, int flags, int startid) { 
  int returnvalue = Super.onstartcomman D (Intent, flags, startid); 
  if (CONSTANTS.LOGVV) { 
    log.v (Constants.tag, Service onStart); 
  } 
  Mlaststartid = Startid; 
  Enqueueupdate (); 
  return returnvalue; 
} 

In the Enqueueupdate function, we send a msg_update message to Mupdatehandler,

private void Enqueueupdate () { 
  mupdatehandler.removemessages (msg_update); 
  Mupdatehandler.obtainmessage (Msg_update, Mlaststartid,-1). Sendtotarget (); 
} 

Received and processed in Mupdatecallback:

Private Handler.callback Mupdatecallback = new Handler.callback () { 
    @Override public 
    boolean handlemessage ( Message msg) { 
      process.setthreadpriority (process.thread_priority_background); 
      Final int startid = MSG.ARG1; 
      Final Boolean isactive; 
      Synchronized (mdownloads) { 
        isactive = updatelocked (); 
      } 
      ...... 
      if (isactive) { 
//if active, the msg_final_update message is sent after delayed 5x60000ms, mainly for "any finished operations Didn ' t trigger a update pass. " 
        Enqueuefinalupdate (); 
      } else { 
//If no active task is in progress, the service is stopped and other 
        if (Stopselfresult (startid) { 
          if (debug_lifecycle) log.v (TAG, "nothing left; Stopped "); 
          Getcontentresolver (). Unregistercontentobserver (mobserver); 
          Mscanner.shutdown (); 
          Mupdatethread.quit (); 
        } 
      return true; 
    } 
  ; 

The point here is the updatelocked () function


  Private Boolean updatelocked () {final Long now = Msystemfacade.currenttimemillis (); 
    Boolean isactive = false; 
Long nextactionmillis = Long.max_value; Mdownloads initialization is an empty Map<long, downloadinfo> final set<long> staleids = Sets.newhashset (mDownloads.keySet 
 
    ()); 
Final Contentresolver resolver = Getcontentresolver (); Get all Downloads tasks final Cursor Cursor = resolver.query (Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, NULL, NULL 
    , NULL, NULL); 
      try {final Downloadinfo.reader Reader = new Downloadinfo.reader (resolver, cursor); 
Final int idcolumn = Cursor.getcolumnindexorthrow (downloads.impl._id); 
        Iteration Download Cusor while (Cursor.movetonext ()) {final Long id = cursor.getlong (idcolumn); 
 
        Staleids.remove (ID); 
Downloadinfo info = mdownloads.get (ID); 
          At the beginning, mdownloads is not content, info==null if (info!= null) {//update the latest download info from the database to monitor the changes in the database and react to the interface Updatedownload (Reader, info, now); 
        else {//Add the newly downloaded Dwonload info to mdownloads, and read the new Dwonload info from the database = insertdownloadlocked (reader, now); 
}//The mdeleted parameter here indicates that when I delete the content that is or has already been downloaded, the database first updates this info.mdeleted to true instead of deleting the file directly if (info.mdeleted) { Do not explain the delete function in detail, primarily by deleting the database contents and the current file contents if (! 
          Textutils.isempty (Info.mmediaprovideruri)) {Resolver.delete (Uri.parse (Info.mmediaprovideruri), NULL, NULL); 
          } deletefileifexists (Info.mfilename); 
 
        Resolver.delete (Info.getalldownloadsuri (), NULL, NULL); 
 
          else {//Start download file final Boolean activedownload = Info.startdownloadifready (mexecutor); 
          Start Media Scanner Final Boolean activescan = Info.startscanifready (Mscanner); 
          IsActive |= activedownload; 
        IsActive |= Activescan; }//Keep track of nearest next action Nextactionmillis = Math.min (now), Info.nextactionmillis IonmIllis); 
    finally {cursor.close (); 
    }//clean up stale downloads so disappeared for (Long id:staleids) {deletedownloadlocked (ID); 
    }//Update notifications visible to User Mnotifier.updatewith (Mdownloads.values ()); if (Nextactionmillis > 0 && nextactionmillis < long.max_value) {final Intent Intent = new Intent (Con Stants. 
      Action_retry); 
      Intent.setclass (this, downloadreceiver.class); Malarmmanager.set (Alarmmanager.rtc_wakeup, now + Nextactionmillis, Pendingintent.getbroadcast) (this, 0, intent, 
    Pendingintent.flag_one_shot)); 
  return isactive; 

 }

Focus on the download of the file, Startdownloadifready function:


 public boolean Startdownloadifready (Executorservice executor) { 
    synchronized (this) { 
      Final Boolean isready = Isreadytodownload (); 
      Final Boolean isactive = Msubmittedtask!= null &&!msubmittedtask.isdone (); 
      if (IsReady &&!isactive) { 
//update the task status of the database to status_running 
        if (mstatus!= impl.status_running) { 
          Mstatus = impl.status_running; 
          Contentvalues values = new Contentvalues (); 
          Values.put (Impl.column_status, mstatus); 
          Mcontext.getcontentresolver (). Update (Getalldownloadsuri (), values, NULL, NULL); 
Start Download task 
        Mtask = new Downloadthread ( 
            mcontext, Msystemfacade, this, Mstoragemanager, mnotifier); 
        Msubmittedtask = Executor.submit (Mtask); 
      } 
      Return IsReady 
    } 
  } 

In the process of downloadthread, if the state of HTTP is OK, it will be handled transferdate.

private void TransferData (state State, HttpURLConnection conn) throws Stoprequestexception { 
... 
in = Conn.getinputstream (); 
...... 
Get InputStream and OutputStream 
if (downloaddrmhelper.isdrmconvertneeded (State.mmimetype)) { 
          drmclient = new Drmmanagerclient (mcontext); 
          Final Randomaccessfile file = new Randomaccessfile ( 
              new File (State.mfilename), "RW"); 
          out = new Drmoutputstream (drmclient, file, state.mmimetype); 
          OUTFD = File.getfd (); 
        } else {out 
          = new FileOutputStream (State.mfilename, true); 
          OUTFD = ((FileOutputStream) out). GETFD (); 
        } 
...... 
Start streaming data, periodically watch for pause/cancel 
      //commands and checking disk spaces as needed. 
      TransferData (state, in, out); 
...... 
} 

------

private void TransferData (state state, InputStream in, outputstream out) 
      throws Stoprequestexception { 
    final byte data[] = new Byte[constants.buffer_size]; 
    for (;;) { 
//read content from InputStream, "in.read (data)", and update the file download size in the database 
      int bytesread = Readfromresponse (state, data, in) ; 
      if (bytesread = = 1) {//success, end of stream already reached 
        handleendofstream (state); 
        return; 
      } 
      State.mgotdata = true; 
Using the OutputStream write-read InputStream, "Out.write (data, 0, bytesread)" 
      writedatatodestination (state, data, Bytesread, out); 
      State.mcurrentbytes + = Bytesread; 
      ReportProgress (state); 
      } 
      Checkpausedorcanceled (state); 
    } 
   

Now that the process of downloading the file is finished, go back to Downloadservice's updatelocked () function, focusing on the Downloadnotifier updatewith () function, This method is used to update notification

//This code is based on different states to set different notification icon if (type = = type_active) {BUILDER.SETSM Allicon (Android. 
      R.drawable.stat_sys_download); else if (type = = type_waiting) {Builder.setsmallicon (Android). 
      r.drawable.stat_sys_warning); else if (type = = Type_complete) {Builder.setsmallicon (Android). 
      R.drawable.stat_sys_download_done); } 
This code is based on different states to set different notification Intent//Build Action intents if (type = = Type_active | | type = = type_waiting) {//Build a synthetic URI for intent identification purposes final URI URI = new Uri.builder (). Scheme (" 
        Active-dl "). Appendpath (TAG). build (); 
        Final Intent Intent = new Intent (constants.action_list, Uri, Mcontext, Downloadreceiver.class); 
        Intent.putextra (Downloadmanager.extra_notification_click_download_ids, Getdownloadids (cluster));  
        Builder.setcontentintent (Pendingintent.getbroadcast (mcontext, 0, Intent, pendingintent.flag_update_current)); 
 
      Builder.setongoing (TRUE); 
        else if (type = = Type_complete) {Final Downloadinfo info = Cluster.iterator (). Next (); 
        Final URI uri = Contenturis.withappendedid (Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, Info.mid); 
 
        Builder.setautocancel (TRUE); 
        Final String Action; if (DownloAds. 
        Impl.isstatuserror (Info.mstatus)) {action = Constants.action_list; else {if (info.mdestination!= Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) {action = Con Stants. 
          Action_open; 
          else {action = constants.action_list; 
        } final Intent Intent = new Intent (action, Uri, Mcontext, Downloadreceiver.class); 
        Intent.putextra (Downloadmanager.extra_notification_click_download_ids, Getdownloadids (cluster));  
 
        Builder.setcontentintent (Pendingintent.getbroadcast (mcontext, 0, Intent, pendingintent.flag_update_current)); Final Intent hideintent = new Intent (Constants.action_hide, Uri, Mcontext, Downloadreceiver.class 
        ); 
      Builder.setdeleteintent (Pendingintent.getbroadcast (mcontext, 0, hideintent, 0)); 

 }


This code is an update download of progress 
if (Total > 0) { 
          final int percent = (int) ((current *)/total); 
          Percenttext = res.getstring (r.string.download_percent, percent); 
 
          if (Speed > 0) { 
            final long Remainingmillis = (total-current) * 1000)/speed; 
            Remainingtext = res.getstring (r.string.download_remaining, 
                dateutils.formatduration (RemainingMillis)); 
 
          Builder.setprogress (m, percent, false); 
        } else { 
          builder.setprogress (0, true); 
        } 

Finally call Mnotifmanager.notify (tag, 0, Notif) and set different notification title and description according to different states.

Thank you for reading, I hope to help you, thank you for your support for this site!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.