我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android DownloadProvider 源码详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android DownloadProvider 源码详解

Android DownloadProvider 源码分析:

Download的源码编译分为两个部分,一个是DownloadProvider.apk, 一个是DownloadProviderUi.apk.

这两个apk的源码分别位于

packages/providers/DownloadProvider/ui/class="lazy" data-src
packages/providers/DownloadProvider/class="lazy" data-src

其中,DownloadProvider的部分是下载逻辑的实现,而DownloadProviderUi是界面部分的实现。

然后DownloadProvider里面的下载虽然主要是通过DownloadService进行的操作,但是由于涉及到Notification的更新,下载进度的展示,下载的管理等。

所以还是有不少其它的类来分别进行操作。

DownloadProvider --  数据库操作的封装,继承自ContentProvider;
DownloadManager -- 大部分逻辑是进一步封装数据操作,供外部调用;
DownloadService -- 封装文件download,delete等操作,并且操纵下载的norification;继承自Service;
DownloadNotifier -- 状态栏Notification逻辑;
DownloadReceiver -- 配合DownloadNotifier进行文件的操作及其Notification;
DownloadList -- Download app主界面,文件界面交互;

下载一般是从Browser里面点击链接开始,我们先来看一下Browser中的代码

在browser的class="lazy" data-src/com/Android/browser/DownloadHandler.Java函数中,我们可以看到一个很完整的Download的调用,我们在写自己的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())); 
  } catch (Exception e) { 
    // This only happens for very bad urls, 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 be set to one of several Environment.DIRECTORY* dirs 
  // depending on mimetype? 
  try { 
    setDestinationDir(downloadPath, filename, request); 
  } catch (Exception e) { 
    showNoEnoughMemoryDialog(activity); 
    return; 
  } 
  // let this downloaded file be scanned by MediaScanner - so that it can 
  // show up in Gallery app, for example. 
  request.allowScanningByMediaScanner(); 
  request.setDescription(webAddress.getHost()); 
  // XXX: Have to use the old url since the cookies were stored using the 
  // old percent-encoded url. 
  String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing); 
  request.addRequestHeader("cookie", 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); 
} 

在这个操作中,我们看到添加了request的各种参数,然后最后调用了DownloadManager的enqueue进行下载,并且在开始后,弹出了开始下载的这个toast。manager是一个DownloadManager的实例,DownloadManager是存在与frameworks/base/core/java/android/app/DownloadManager.java。可以看到enqueue的实现为:


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; 

enqueue函数主要是将Rquest实例分解组成一个ContentValues实例,并且添加到数据库中,函数返回插入的这条数据返回的ID;ContentResolver.insert函数会调用到DownloadProvider实现的ContentProvider的insert函数中去,如果我们去查看insert的code的话,我们可以看到操作是很多的。但是我们只需要关注几个关键的部分:


...... 
//将相关的请求参数,配置等插入到downloads数据库; 
long rowID = db.insert(DB_TABLE, null, filteredValues); 
...... 
//将相关的请求参数,配置等插入到request_headers数据库中; 
insertRequestHeaders(db, rowID, values); 
...... 
if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) == 
        Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) { 
      // When notification is requested, kick off service to process all 
      // relevant downloads. 
//启动DownloadService进行下载及其它工作 
      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); 

在这边,我们就可以看到下载的DownloadService的调用了。因为是一个startService的方法,所以我们在DownloadService里面,是要去走oncreate的方法的。


@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); 
} 

这边的话,我们可以看到先去启动了一个handler去接收callback的处理


mUpdateThread = new HandlerThread(TAG + "-UpdateThread"); 
 mUpdateThread.start(); 
 mUpdateHandler = new Handler(mUpdateThread.getLooper(), mUpdateCallback); 

然后去


getContentResolver().registerContentObserver(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, 
        true, mObserver) 

是去注册监听Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI的Observer。
而oncreate之后,就会去调用onStartCommand方法.


@Override 
ublic int onStartCommand(Intent intent, int flags, int startId) { 
  int returnValue = super.onStartCommand(intent, flags, startId); 
  if (Constants.LOGVV) { 
    Log.v(Constants.TAG, "Service onStart"); 
  } 
  mLastStartId = startId; 
  enqueueUpdate(); 
  return returnValue; 
} 

在enqueueUpdate的函数中,我们会向mUpdateHandler发送一个MSG_UPDATE Message,


private void enqueueUpdate() { 
  mUpdateHandler.removeMessages(MSG_UPDATE); 
  mUpdateHandler.obtainMessage(MSG_UPDATE, mLastStartId, -1).sendToTarget(); 
} 

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) { 
//如果Active,则会在Delayed 5×60000ms后发送MSG_FINAL_UPDATE Message,主要是为了“any finished operations that didn't trigger an update pass.” 
        enqueueFinalUpdate(); 
      } else { 
//如果没有Active的任务正在进行,就会停止Service以及其它 
        if (stopSelfResult(startId)) { 
          if (DEBUG_LIFECYCLE) Log.v(TAG, "Nothing left; stopped"); 
          getContentResolver().unregisterContentObserver(mObserver); 
          mScanner.shutdown(); 
          mUpdateThread.quit(); 
        } 
      } 
      return true; 
    } 
  }; 

这边的重点是updateLocked()函数


  private boolean updateLocked() { 
    final long now = mSystemFacade.currentTimeMillis(); 
    boolean isActive = false; 
    long nextActionMillis = Long.MAX_VALUE; 
//mDownloads初始化是一个空的Map<Long, DownloadInfo> 
    final Set<Long> staleIds = Sets.newHashSet(mDownloads.keySet()); 
    final ContentResolver resolver = getContentResolver(); 
//获取所有的DOWNLOADS任务 
    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); 
//迭代Download Cusor 
      while (cursor.moveToNext()) { 
        final long id = cursor.getLong(idColumn); 
        staleIds.remove(id); 
        DownloadInfo info = mDownloads.get(id); 
//开始时,mDownloads是没有任何内容的,info==null 
        if (info != null) { 
//从数据库更新最新的Download info信息,来监听数据库的改变并且反应到界面上 
          updateDownload(reader, info, now); 
        } else { 
//添加新下载的Dwonload info到mDownloads,并且从数据库读取新的Dwonload info 
          info = insertDownloadLocked(reader, now); 
        } 
//这里的mDeleted参数表示的是当我删除了正在或者已经下载的内容时,首先数据库会update这个info.mDeleted为true,而不是直接删除文件 
        if (info.mDeleted) { 
//不详细解释delete函数,主要是删除数据库内容和现在文件内容 
          if (!TextUtils.isEmpty(info.mMediaProviderUri)) { 
        resolver.delete(Uri.parse(info.mMediaProviderUri), null, null); 
          } 
          deleteFileIfExists(info.mFileName); 
          resolver.delete(info.getAllDownloadsUri(), null, null); 
        } else { 
          // 开始下载文件 
          final boolean activeDownload = info.startDownloadIfReady(mExecutor); 
          // 开始media scanner 
          final boolean activeScan = info.startScanIfReady(mScanner); 
          isActive |= activeDownload; 
          isActive |= activeScan; 
        } 
        // Keep track of nearest next action 
        nextActionMillis = Math.min(info.nextActionMillis(now), nextActionMillis); 
      } 
    } finally { 
      cursor.close(); 
    } 
    // Clean up stale downloads that 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(Constants.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; 
  } 

重点来看看文件的下载,startDownloadIfReady函数:


 public boolean startDownloadIfReady(ExecutorService executor) { 
    synchronized (this) { 
      final boolean isReady = isReadyToDownload(); 
      final boolean isActive = mSubmittedTask != null && !mSubmittedTask.isDone(); 
      if (isReady && !isActive) { 
//更新数据库的任务状态为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); 
        } 
//开始下载任务 
        mTask = new DownloadThread( 
            mContext, mSystemFacade, this, mStorageManager, mNotifier); 
        mSubmittedTask = executor.submit(mTask); 
      } 
      return isReady; 
    } 
  } 

在DownloadThread的处理中,如果HTTP的状态是ok的话,会去进行transferDate的处理。


private void transferData(State state, HttpURLConnection conn) throws StopRequestException { 
...... 
in = conn.getInputStream(); 
...... 
//获取InputStream和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 space 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 (;;) { 
//从InputStream中读取内容信息,“in.read(data)”,并且对数据库中文件下载大小进行更新 
      int bytesRead = readFromResponse(state, data, in); 
      if (bytesRead == -1) { // success, end of stream already reached 
        handleEndOfStream(state); 
        return; 
      } 
      state.mGotData = true; 
//利用OutPutStream写入读取的InputStream,"out.write(data, 0, bytesRead)" 
      writeDataToDestination(state, data, bytesRead, out); 
      state.mCurrentBytes += bytesRead; 
      reportProgress(state); 
      } 
      checkPausedOrCanceled(state); 
    } 
  } 

至此,下载文件的流程就说完了,继续回到DownloadService的updateLocked()函数中来;重点来分析DownloadNotifier的updateWith()函数,这个方法用来更新Notification


//这段代码是根据不同的状态设置不同的Notification的icon 
 if (type == TYPE_ACTIVE) { 
        builder.setSmallIcon(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); 
      } 

//这段代码是根据不同的状态来设置不同的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 = Constants.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)); 
      } 

//这段代码是更新下载的Progress 
if (total > 0) { 
          final int percent = (int) ((current * 100) / 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(100, percent, false); 
        } else { 
          builder.setProgress(100, 0, true); 
        } 

最后调用mNotifManager.notify(tag, 0, notif);根据不同的状态来设置不同的Notification的title和description

 感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

您可能感兴趣的文章:Android应用开发SharedPreferences存储数据的使用方法android TextView设置中文字体加粗实现方法Android 动画之TranslateAnimation应用详解android PopupWindow 和 Activity弹出窗口实现方式android压力测试命令monkey详解解决Android SDK下载和更新失败的方法详解android客户端从服务器端获取json数据并解析的实现代码android listview优化几种写法详细介绍android调试工具DDMS的使用详解


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android DownloadProvider 源码详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android DownloadProvider 源码详解

Android DownloadProvider 源码分析: Download的源码编译分为两个部分,一个是DownloadProvider.apk, 一个是DownloadProviderUi.apk.这两个apk的源码分别位于packa
2022-06-06

Android Matrix源码详解

Matrix的数学原理 在Android中,如果你用Matrix进行过图像处理,那么一定知道Matrix这个类。Android中的Matrix是一个3 x 3的矩阵,其内容如下: Matrix的对图像的处理可分为四类基本变换:Transla
2022-06-06

Android源码解析之属性动画详解

前言 大家在日常开发中离不开动画,属性动画更为强大,我们不仅要知道如何使用,更要知道他的原理。这样,才能得心应手。那么,今天,就从最简单的来说,了解下属性动画的原理。ObjectAnimator.ofInt(mView,"width",10
2022-06-06

Android线程间通信Handler源码详解

这篇文章主要为大家介绍了Android线程间通信Handler源码示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Android实现屏幕锁定源码详解

最近有朋友问屏幕锁定的问题,自己也在学习,网上找了下也没太详细的例子,看的资料书上也没有有关屏幕锁定程序的介绍,下个小决心,自己照着官方文档学习下,现在做好了,废话不多说,先发下截图,看下效果,需要注意的地方会加注释,有问题的朋友可以直接留
2022-06-06

Android源码中的目录结构详解

Android 2.1 |-- Makefile |-- bionic (bionic C库) |-- bootable (启动引导相关代码) |-- build
2022-06-06

Android context源码详解及深入分析

Android context详解 前言: Context都没弄明白,还怎么做Android开发? Activity mActivity =new Activity()作为Android开发者,不知道你有没有思考过这个问题,Activity
2022-06-06

Android Studio查看Android 5.x源码的步骤详解

关于Android Studio的好处我就不用说了,下面两点就足矣让你转投Android Studio了: 1、Android Studio是Google官方指定的,目前官网已经去掉了ADT, 大家可以在Android开发者官网
2022-06-06

Android ViewPager源码详细分析

1.问题 由于Android Framework源码很庞大,所以读源码必须带着问题来读!没有问题,创造问题再来读!否则很容易迷失在无数的方法与属性之中,最后无功而返。 那么,关于ViewPager有什么问题呢? 1). setOffsre
2022-06-06

Android 简易手势密码开源库详解

简介 本文介绍一个Android手势密码开源库的使用及实现的详细过程,该开源库主要实现以下几个功能:支持手势密码的绘制,并支持密码保存功能,解锁时自动比对密码给出结果封装了绘制密码的方法,比对两次密码是否一致,可以快捷地进行手势密码的设置可
2022-06-06

Windows下获取Android 源码方法的详解

前言:略!获取源码的原因千千万~~~ 1.安装GIT工具。GIT是林纳斯·托瓦兹大神为了管理器Linux内核开发而创立的分布式版本控制软件。下载地址:http://code.google.com/p/msysgit/一路next将安装进行到
2022-06-06

Android源码-使用Android Studio 阅读Android Framework源码

前言 Android 系统源码比较浩瀚,各级目录繁多,有没有办法将源码导入到Studio之中,进行阅读呢?答案是有的。 源码提供了相关的编译方式。1.生成idegen.jar文件。 在源码根目录下输入指令: xx@xx:~/code/and
2022-06-06

Android开发数据结构算法ArrayList源码详解

这篇文章主要为大家介绍了Android开发数据结构算法ArrayList源码详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Android 网络html源码查看器详解及实例

Android 网络html源码查看器详解及实例 IO字节流的数据传输了解Handler的基本使用1.作品展示 2.需要掌握的知识 FileInputStream,FIleOutputStream,BufferInputStream,Buf
2022-06-06

Android编程单元测试实例详解(附源码)

本文实例讲述了Android编程单元测试。分享给大家供大家参考,具体如下: 完整实例代码代码点击此处本站下载。 本文是在上一篇文章《java编程之单元测试(Junit)实例分析》的基础上继续讲解android的单元测试,android源码中
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录