skyscnnerr Python怎么体现

类别: &贡献者: &总点击:1247 &受欢迎度:10
知识概况:
embroidary-
embroidery-
embroidery-
embroidry-
embroidry-
embroidary-embroidery-embroidery-embroidry-embroidry-<<<<<<<<<<<<<<<<<<<<english-<<<<<<<<<<<<<<<<<<<<<<e-<<<<<<<<<<<<<<<<<<<<--<<<<es----<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<-<<<<<<<<<exclusive-images-<<<<<<<<<<<<<<<<<<<extraextra-.com<<<<<eyeofthemind-.com<<<<<<<<f1challenge-<<<fabric-and-<<<<<<<<<<facialmovies-.com<<<<<<<<<<<<<<<<<<<<<<<<<f--<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<federal-federal-<<<<<<<<feirainternacionaldopant-.com<<<<<<<<<<<<<<<<<<<<<<<<filing-<<<<<<<findip-.com<<<<<<<firegems-n-<<<<<<<<<<<<<<<<<<<fjn-.com<<<<<<<<<<<<<<<<-sofancy-regy-<<<<<<flogger-.com<<<<florida-florida-florida-florida-florida-florida-florida-florida-florida-florida-florida-florida-florida-florida-florida-vacation-homes-<<<<<<<<flower-flower-flower-flower---flower---flowers-<<<<<<<<<<<<<<<<<<<<<<<<<<-d--.comforclosure-<foreclosures-foreclosures-homes-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<fraz-.com<<<<<<-----animated---animated------------------s.com----donload--es.com--downloadable---downloadable---downlod--es.com----e-mail---electronic---email---embriodery---embriodery-----embroidery---embroidery-----forclosure-----------greeting-cards-----hand---hand---hand---hand---hand-<<<<<<<<<<<<<<<neesma-.com<<<<<<<<<<<<<<<<nesly-<<<<<<<<<<<<<<<<<<<<<<<<<<newer-.com<<<<<<<<<<<<<<<<<<<<<<newnewnew-.comnewportbeachnews-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<nicksauto-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<nitco-.com<<<<<<<<<<<<<<<<<<<<<<<<<<nodar-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<northwestph-.com<<<<<norwf2a-.com<<<<<<<<<<<<<<nothstarfood-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<--------<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<office-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<olegoatf-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<--hard-.com--internet---internet---internet---------movies---movies---movies---movies-------mp3---mp3---------online---printable-----------------house-.com----------------<<<<<<<<<<<<<<<<<<<fu-<<<<<<<<<<full-length-dawnload-movies--.comfull-length-donload-movies--.comfull-length-donwload-movies--.comfull-length-downlaod-movies--.comfull-length-downloade-movies--.comfull-length-downlod-movies--.com<<<<<<funny-answering-funny-funny-<<furniture-<<<<<<<<<<<<<<<<<<<fzaa3-.com<<<g5-assessoria-e-<<<<<<<<<<<<<<----------------<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<gestational-gestational-gestational-gestational-<get-get---get---get-<<<<<<<<<<<<<<<<<<ghost-<<<<<gigs-<<<<<<<giramagica-.com<-o-.com-<<<<<<gl--e.com<<<<<<<<<<<<<<<<<<<<<<glyn-.comglzhi-.com<<<<<<go-go-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<government-student-<<<<<<<<<<<<<<<<<grand-genesis-<<<<<<<<<<<great-<<<<greating-greating-<<<<<greeding-<<<<<<<<<<<<<<gregorymason-<<greting-greting-greting-gretting-<<<<<<<<<<<<<<<<<<<<gsm-.com<<<<<<<<<<guide-guide-guide-guide-to-guide-<<<<<<guit-<<<<<<<<<<<guru-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<-----<hair-<<<<<<<<<<<<<<<<<<<<<hand-hand-hand-hand-hand-hand-hand-embroidary---hand-hand-handmade-handmade-handmade-handmade-<<<<<<<<<<hanse--<<<<<hardcoregra-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<health-and-wealth-health-health-health-health-health-<<<<<<<<hear-<<<<<<<<<hey-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<hide-<higashihiroshima-<<<<<<<<hiltonmortgage-.com<<<<<<<<<<hit-<<<<<<<<<<<<<<<<<<<hnhong-.com<<<<<<<<<<<<<<<<<<<<<<<<home-based-business-home-home-home-home-impr--s.comhome-home-home-<<homecomfort-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<hot-<<<<<<<<<<<<<<<<<<<<<<house-<houses-and-homes-<how-to-how-to-how-how-to-<<<<<<<<<<<<<<<<<<<<<<<ht-.com<<<<<<<<huaxi-.com<<<<<<<<<<<<<<<<<<hurricane-<<<<<<<<<<<<hydroponic-<<<<<<<<<<<i--<<<<<<<<<<<<<<<<<<<<<<<<<<<idol--.com<<<<<<<<<ifm-<<<if-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<in-<incc-.comincesto-.com<<<indiana-personal-<<<individual-<<<<<<<<<<<<<<<<<<<<<<<<<<<institut-instructions-for-instructions-for-<<<<<<<intensere-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<iron-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<iwakura-<<<<<<<<<<<<<j--<<<<<<<<<<<<<<<jajajajajdracualqierboludooiendiatieneclubdefanss.com<<<<<<<<<<<<<<<<<<<jay--<<<<<<<<<<<<<<<<<<jeans-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<--<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<joanne-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<joseph-<<<<<<<<<<<<<<<<js-<<<<<<<<<<<<<<<<<<<<<juicy-.com<<<<<<<<<<<<<<<justiin-.comjustin---<<<<<<juvenile-<<<<<<<<<<<jzhk-.com<<<k2--s.com<<<<<<<<<<<<<<<<<<<<<<<<<karin-<<<<<<<<<<<<<<<kayahuse-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<kerala20-<<<<<<<<<<<<<<<<<kid-.com<<<<<<<kingsizenet-.com<<<<<<<<<<<<<<<<<<<<<<<<<<klse-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ks-ba-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<lake-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<latest-<<<<<<<<linia-<<<<<<<<<<<<<<<<<<<<<lead--<<<<<learn-learn-<<<<<<<<<<<<<<<<leitnerph-.com<<<<<<<<<<<les-<<<<<<<<<<<<<<<<<<lexifl-.com<<<<<<<<<<<<<li-<<li--.com<<<<life-<<<<<<<<<<<<<<<<<<<<<<<link-<<<<<<<<<<<<<<<<<liverwrestling-.com<livewrestiling-.comlivewrestlin-.com<<<<<<lk--he.com<<<<<<<<<<<<<<<<<<-motion-.com<<<<<<<<<<<<long-term-<<<<lords-<<<<<<<<<<<<<<<<<<<<<<<<<low-cost-low-<<<<<<<<<<<<<<<luggage-<<<<<<<-luther-<<<<<<<<<<<lya-.com<<<<<<<<<m-y-p-o-k-<<<<<<<<<<machine-machine-machine-machine-guns-<<<<<<<<<<<<<<<<<magical-<<<<<<<<<<<<<<<<<<<<<<<make-extra-<<<<<<<<<<<<management-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<marketing-.com<<<<<<<<m-<<<<martialart-.com<<<<<<<<<<<<<<<<massage-<<<<<maste-.com<<<<<<<<<<<<<<<<<<<<<-maui-jim-<<<<maxima51-.com<<mazica-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<-----cal-handouts-for--------terraneanproperty-.com<<<<<<<<<<<<<<<<<<<<<<memories-<<<<<<<<<<<<<<<<<<<<<<<<<<metro-tech--s.com<<<<<<<<<<<<<<<<<<<mickymath-<microwes-<<<<midi-files---midi-files---midi-files---<<<<mighy-.com<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<miracle-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<mjc-club-des-<<<<<<<<<<<mnskyline-.com<<<mobile-mobile-<mobilephone--<<<<<mode-<<<<<<<<<<<<<<<<<<<<<<moneybookers-<<<<<<<<<<<<<<<<<<<<<<<<mortgage-mortgage-loan-<<<<<moto-<<<motorcycle-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<mu---.com<<<<muffin-<<<<<<<<<<<<<<<<<<<<music-<<<<<<<<<<my-account-bank-my-<<<<<<<<<<<mybest-.com<<<<<<<<<<<<<<<<<<my-<<<<<<<<<<<<<<<<<<<<<<myylol-.com<<<<<<<<<<<<<<<<<<n-e-<<<<<<<<<<推荐当前文章:
《Domain Names Registered on Nov 21_13,2008》,由域名网·中国编辑整理;志愿者每天为您分享实用的域名知识,转载本文,请注明出处:/view/53555. 欢迎您再次访问本站,有您的参与,互联网生活会更精彩!
相关目录: >
收藏到: &
(提示:顶到域名网·中国首页,分享给更多人)&&
&&&用Google翻译此网页:
现在有0人对'Domain Names Registered on Nov 21_13,2008'发表评论
您的姓名:
* 可选项,留空即为匿名发表
评论内容:
剩余字数:& * 按 Ctrl + Enter 直接发送.
域名网·中国温馨提示:评论只需提交一次,请耐心等候,审核后方可显示.
精彩域名知识分享
域名知识大学堂
域名信息网米友推荐
&&&&&&&&&&
声明:本站为我们域名爱好者共同分享知识的平台,非域名注册机构.android系列(62)
转自: http://blog.csdn.net/Innost/article/details/6083467
MediaScanner分析
一 MediaScannerService
多媒体扫描是从MediaScannerService开始的。这是一个单独的package。位于
packages/providers/MediaProvider:含以下java文件
l&&&&&&&& MediaProvider.java
l&&&&&&&& MediaScannerReceiver.java
l&&&&&&&& MediaScannerService.java
l&&&&&&&& MediaThumbRequest.java
分析这个目录的Android.mk文件,发现它运行的进程名字就是android.process.media。
application android:process=android.process.media
1.1&&& MediaScannerReceiver
这个类从BroadcastReceiver中派生,用来接收任务的。
MediaScannerReceiver extends BroadcastReceiver
在它重载的onRecieve函数内有以下几种走向:
if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
&&&&&& &&&&&// 收到”启动完毕“广播后,扫描内部存储
&&&&&&&&&&& scan(context, MediaProvider.INTERNAL_VOLUME);
&&&&&&& } else {
&&&&&&&&&&& ……….
&&&&&&&&&&&&&&& if (action.equals(Intent.ACTION_MEDIA_MOUNTED) &&
&&&&&&&&&&&&&&&&&&&&&&& externalStoragePath.equals(path)) {
&&&&&&&&&&&&&& /收到MOUNT信息后,扫描外部存储
&&&&&&&&&&&&&&&&&&& scan(context, MediaProvider.EXTERNAL_VOLUME);
&&&&&&&&&&&&&&& }
&else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&
&&&&&&&&&&&&&&&&&&&&&&& path != null && path.startsWith(externalStoragePath &#43; &/&)) {
&&&&&&&&&&&&&& //收到请求要求扫描某个文件,注意不会扫描内部存储上的文件
&&&&&&&&&&&&&&&&&&& scanFile(context, path);
&&&&&&& …………………………..
……下面是它调用的scan函数:
scan(Context context, String volume)
Bundle args = new Bundle();
&&&&&&& args.putString(&volume&, volume);
//直接启动MediaScannerService了,
&&&&&&& context.startService(
&&&&&&&&&&&&&&& new Intent(context, MediaScannerService.class).putExtras(args));
MediaScannerReceiver是用来接收任务的,它收到广播后,会启动MediaService进行扫描工作。
下面看看MediaScannerService.
1.2&&& MediaScannerService
MSS标准的从Service中派生下来,
MediaScannerService extends Service implements Runnable
//注意:是一个Runnable…,可能有线程之类的东西存在
下面从Service的生命周期的角度来看看它的工作。
1. onCreate
public void onCreate()
&&&&&& PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
&&&&&&& mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
&&&&& //获得电源锁,防止在扫描过程中休眠
&&&&& //单独搞一个线程去跑扫描工作,防止ANR
&&&& Thread thr = new Thread(null, this, &MediaScannerService&);
&&&&&&& thr.start();
2. onStartCommand
&&& public int onStartCommand(Intent intent, int flags, int startId)
&&&&& //注意这个handler,是在另外一个线程中创建的,往这个handler里sendMessage
&&&& //都会在那个线程里边处理
& //不明白的可以去查看handler和Looper机制
//这里就是同步机制,等待mServiceHandler在另外那个线程创建完毕
while (mServiceHandler == null) {
&&&&&&&&&&& synchronized (this) {
&&&&&&&&&&&&&&& try {
&&&&&&&&&&& &&&&&&&&wait(100);
&&&&&&&&&&&&&&& } catch (InterruptedException e) {
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }
&&&&&&& if (intent == null) {
&&&&&&&&&&& Log.e(TAG, &Intent is null in onStartCommand: &,
&&&&&&&&&&&&&&& new NullPointerException());
&&&&&& &&&&&return Service.START_NOT_STICKY;
&&&&&&& Message msg = mServiceHandler.obtainMessage();
&&&&&&& msg.arg1 = startId;
&&&&&&& msg.obj = intent.getExtras();
//把MediaScannerReceiver发出的消息传递到另外那个线程去处理。
&&&&&&& mServiceHandler.sendMessage(msg);
&& && ………….
基本上MSR(MediaScannerReceiver)发出的请求都会传到onStartCommand中处理。如果有多个存储的话,也只能一个一个扫描了。
下面看看那个线程的主函数
public void run()
&&&&&&& // reduce priority below other background threads to avoid interfering
&&&&&&& // with other services at boot time.
&&&&&&& Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND &#43;
&&&&&&&&&&&&&&& Process.THREAD_PRIORITY_LESS_FAVORABLE);
//不明白的去看看Looper和handler的实现
&&&&&&& Looper.prepare();//把这个looper对象设置到线程本地存储
&&&&&&& mServiceLooper = Looper.myLooper();
&&&&&&& mServiceHandler = new ServiceHandler();//创建handler,默认会把这个looper
//的消息队列赋&#20540;给handler的消息队列,这样往handler中发送消息就是往这个线程的looper发
&&&&&&& Looper.loop();//消息循环,内部会处理消息队列中的消息
//也就是handleMessage函数
上面handler中加入了一个扫描请求(假设是外部存储的),所以要分析handleMessage函数。
4. handleMessage
private final class ServiceHandler extends Handler
&&&&&&& @Override
&&&&&&& public void handleMessage(Message msg)
&&&&&&&&&&& Bundle arguments = (Bundle) msg.
&&&&&&&&&&& String filePath = arguments.getString(&filepath&);
&&&&&&&&&&&
&&&&&&&&&&& try {
……… 这里不讲了
&&&&&&&&&&&&&&& } else {
&&&&&&&&&&&&&&&&&&& String volume = arguments.getString(&volume&);
&&&&&&&&&&&&&&&&&&& String[] directories =
&&&& &&&&&&&&&&&&&&&if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
&&&&&&&&&&&&&&&&&&&& //是扫描内部存储的请求?&&
// scan internal media storage
&&&&&&&&&&&&&&&&&&&&&&& directories = new String[] {
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Environment.getRootDirectory() &#43; &/media&,
&&&&&&&&&&&&&&&&&&&&&&& };
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&&&&&& else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
&&&&&&&&&&&&&&&&&&&&& //是扫描外部存储的请求?获取外部存储的路径&
&&&&&&&&&&&&&&&&&&&&&& directories = new String[] {
&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&Environment.getExternalStorageDirectory().getPath(),
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& };
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&&&&&& if (directories != null) {
//真正的扫描开始了,上面只不过是把存储路径取出来罢了.
&&&&&&&&&&&&&&&&&&&&&&& scan(directories, volume);
&&&&&&&&&&&&&&&&&&&&&&&& …..
//扫描完了,就把service停止了
&&&&&&&&&&& stopSelf(msg.arg1);
5. scan函数
private void scan(String[] directories, String volumeName) {
&&&&&& mWakeLock.acquire();
//下面这三句话很深奥…
//从 getContentResolver获得一个ContentResover,然后直接插入
//根据AIDL,这个ContentResover的另一端是MediaProvider。只要去看看它的
//insert函数就可以了
//反正这里知道获得了一个扫描URI即可。
& ContentValues values = new ContentValues();
&& values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
&& Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
&&& &&&&Uri uri = Uri.parse(&file://& &#43; directories[0]);
//发送广播,通知扫描开始了
&&&&&&& sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
&&&&&&& try {
&&&&&&&&&&& if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
&&&&&&&&&&&&&&&& openDatabase(volumeName);&&&
&&&&&&&&&&& }
//创建真正的扫描器
&&&&&&&&&&& MediaScanner scanner = createMediaScanner();
//交给扫描器去扫描文件夹& scanDirectories
&&&&&&&&&&& scanner.scanDirectories(directories, volumeName);
&&&&&&& } catch (Exception e) {
&&&&&&& && Log.e(TAG, &exception in MediaScanner.scan()&, e);
//删除扫描路径
&&&&&&& getContentResolver().delete(scanUri, null, null);
//通知扫描完毕
&&&&&&& sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
&&&&&&& mWakeLock.release();
说说上面那个深奥的地方,在MediaProvider中重载了insert函数,insert函数会调用insertInternal函数。
private Uri insertInternal(Uri uri, ContentValues initialValues) {
&&&&&&& long rowId;
&&&&&&& int match = URI_MATCHER.match(uri);
&&&&&&& // handle MEDIA_SCANNER before calling getDatabaseForUri()
//刚才那个insert只会走下面这个分支,其实就是获得一个地址….
//太绕了!!!!!
&&&&&&& if (match == MEDIA_SCANNER) {
&&&&&&&&&&& mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
&&&&&&&&&&& return MediaStore.getMediaScannerUri();
再看看它创建了什么样的Scanner,这就是MSS中的createMediaScanner
private MediaScanner createMediaScanner() {
//下面这个MediaScanner在framework/base/中,待会再分析
&&&&&&& MediaScanner scanner = new MediaScanner(this);
//设置当前的区域,这个和字符编码有重大关系。
&&&&&&& Locale locale = getResources().getConfiguration().
&&&&&&& if (locale != null) {
&&&&&&&&&&& String language = locale.getLanguage();
&&&&&&&&&&& String country = locale.getCountry();
&&&&&&&&&&& String localeString =
&&&&&&&&&&& if (language != null) {
&&&&&&&&&&&&&&& if (country != null) {
//给扫描器设置当前国家和语言。
&&&&&&&&&&&&&&&&&&& scanner.setLocale(language &#43; &_& &#43; country);
&&&&&&&&&&&&&&& } else {
&&&&&&&&&&&&&&&&&&& scanner.setLocale(language);
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }&&&
至此,MSS的任务完成了。接下来是MediaScanner的工作了。
MSS的工作流程如下:
l&&&&&&&& 1 单独启动一个带消息循环的工作线程。
l&&&&&&&& 2 主线程接收系统发来的任务,然后发送给工作线程去处理。
l&&&&&&&& 3 工作线程接收任务,创建一个MediaScanner去扫描。
l&&&&&&&& 4 MSS顺带广播一下扫描工作启动了,扫描工作完毕了。
二 MediaScanner
MediaScanner位置在
frameworks/base/media/下,包括jni和java文件。
先看看java实现。
这个类巨复杂,而且和MediaProvider交互频繁。在分析的时候要时刻回到MediaProvider去看看。
1.&&&&&& 初始化
public class MediaScanner
//libmedia_jni.so的加载是在MediaScanner类中完成的
//这么重要的so为何放在如此不起&#30524;的地方加载???
&&&&&&& System.loadLibrary(&media_jni&);
&&&&&&& native_init();
public MediaScanner(Context c) {
&&&&&&& native_setup();//调用jni层的初始化,暂时不用看了,无非就是一些
//初始化工作,待会在再进去看看
刚才MSS中是调用scanDirectories函数,我们看看这个。
2.&&&&&& scanDirectories
public void scanDirectories(String[] directories, String volumeName) {
&&&&&&& try {
&&&&&&&&&&& long start = System.currentTimeMillis();
&&&&&&&&&&& initialize(volumeName);//初始化
&&&&&&&&&&& prescan(null);//扫描前的预处理
&&&&&&&&&&& long prescan = System.currentTimeMillis();
&&&&&&&&&&& for (int i = 0; i & directories. i&#43;&#43;) {
//扫描文件夹,这里有一个很重要的参数 mClient
// processDirectory是一个native函数
&&&&&&&&&&&&&&& processDirectory(directories[i], MediaFile.sFileExtensions, mClient);
&&&&&&&&&&& }
&&&&&&&&&&& long scan = System.currentTimeMillis();
&&&&&&&&&&& postscan(directories);//扫描后处理
&&&&&&&&&&& long end = System.currentTimeMillis();
&&&&&&&&&& …..打印时间,异常处理…没了…
下面简单讲讲initialize ,preScan和postScan都干嘛了。
private void initialize(String volumeName) {
//打开MediaProvider,获得它的一个实例
&&&&&&& mMediaProvider = mContext.getContentResolver().acquireProvider(&media&);
//得到一些uri
&&&&&&& mAudioUri = Audio.Media.getContentUri(volumeName);
&&&&&&& mVideoUri = Video.Media.getContentUri(volumeName);
&&&&&&& mImagesUri = Images.Media.getContentUri(volumeName);
&&&&&&& mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
//外部存储的话,可以支持播放列表之类的东西,搞了一些个缓存池之类的
//如mGenreCache等
&&&&&&& if (!volumeName.equals(&internal&)) {
&&&&&&&&&&& // we only support playlists on external media
&&&&&&&&&&& mProcessPlaylists =
&&&&&&&&&& mGenreCache = new HashMap&String, Uri&();
&&&&&&&& …
preScan,这个函数很复杂:
大概就是创建一个FileCache,用来缓存扫描文件的一些信息,例如last_modified等。这个FileCache是从MediaProvider中已有信息构建出来的,也就是历史信息。后面根据扫描得到的新信息来对应更新历史信息。
postScan,这个函数做一些清除工作,例如以前有video生成了一些缩略图,现在video文件被干掉了,则对应的缩略图也要被干掉。
另外还有一个mClient,这个是从MediaScannerClient派生下来的一个东西,里边保存了一个文件的一些信息。后续再分析。
刚才说到,具体扫描工作是在processDirectory函数中完成的。这个是一个native函数。
在frameworks/base/media/jni/android_media_MediaScanner.cpp中。
三& MediaScanner JNI层分析
MediaScanner JNI层内容比较多,单独搞一节分析吧。
先看看android_media_MediaScanner这个文件。
1. native_init函数,jni对应的函数如下
static void
android_media_MediaScanner_native_init(JNIEnv *env)
clazz = env-&FindClass(&android/media/MediaScanner&);
//得都JAVA类中mNativeContext这个成员id
&&& fields.context = env-&GetFieldID(clazz, &mNativeContext&, &I&);
&& //不熟悉JNI的自己去学习下吧
3.&&&&&& native_setup函数,jni对应函数如下:
android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
//创建MediaScanner对象
&&& MediaScanner *mp = createMediaScanner();
//太变态了,自己不保存这个对象指针.
//却把它设置到java对象的mNativeContext去保存
&&& env-&SetIntField(thiz, fields.context, (int)mp);
//创建MediaScanner函数
static MediaScanner *createMediaScanner() {
#if BUILD_WITH_FULL_STAGEFRIGHT
//使用google自己的
& return new StagefrightMediaS
#ifndef NO_OPENCORE
//使用opencore提供的
&&& return new PVMediaScanner();
4.&&&&&& processDirectories函数,jni对应如下:
android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client)
&&& MediaScanner *mp = (MediaScanner *)env-&GetIntField(thiz, fields.context);
//每次都要回调到JAVA中去取这个Scanner!!
&&& const char *pathStr = env-&GetStringUTFChars(path, NULL);
&&& const char *extensionsStr = env-&GetStringUTFChars(extensions, NULL);
//又在C&#43;&#43;这里搞一个client,然后把java的client放到C&#43;&#43;Client中去保存
//而且还是栈上的临时变量..
MyMediaScannerClient myClient(env, client);
//scanner扫描文件夹,用得是C&#43;&#43;的client
&&& mp-&processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env);
&&& env-&ReleaseStringUTFChars(path, pathStr);
&&& env-&ReleaseStringUTFChars(extensions, extensionsStr);
到这里&#20284;乎就没有了,那么扫描后的数据库是怎么更新的呢?为什么要传入一个client进去呢?看来必须得trace到scanner中去才知道了。
四 PVMediaScanner
这个在external/opencore/android/mediascanner.cpp中。
1.&&&&&& processDirectory
status_t MediaScanner::processDirectory(const char *path, const char* extensions,
&&&&&&& MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)
&&& InitializeForThread();
&&& int error = 0;
status_t result = PVMFS
//调用client的设置区域函数
&& client.setLocale(mLocale);
//扫描文件夹,咋还没开始??
&& result = doProcessDirectory(pathBuffer, pathRemaining, extensions, client, exceptionCheck, exceptionEnv);
2.&&&&&& doProcessDirectory
status_t MediaScanner::doProcessDirectory(char *path, int pathRemaining, const char* extensions,
&&&&&&& MediaScannerClient& client, ExceptionCheck exceptionCheck, void* exceptionEnv)
…终于看到点希望了
//打开这个文件夹,枚举其中的内容。
//题外话,这个时候FileManager肯定删不掉这个文件夹!!
&&& DIR* dir = opendir(path);
&&& while ((entry = readdir(dir))) {
&&&&&&& const char* name = entry-&d_
//不处理.和..文件夹
&if (isDirectory) {
//不处理.开头的文件夹。如果是文件夹,递归调用doProcessDirectory
//深度优先啊!
&&&&&&&&&&& int err = doProcessDirectory(path, pathRemaining - nameLength - 1, extensions, client, exceptionCheck, exceptionEnv);
&&&&&&&&&&&&&&& if (err) {
&&&&&&&&&&&&&&&&&&& LOGE(&Error processing '%s' - skipping/n&, path);
&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&& }
&&&&&&&&&&& } else if (fileMatchesExtension(path, extensions)) {
//是一个可以处理的文件,交给client处理
//彻底疯掉了….这是干嘛呢???
&&&&&&&&&&&&& client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
这里要解释下,刚才createMediaScanner中,明明创建的是PVMediaScanner,为何这里看得是MediaScanner代码呢?
l&&&&&&&& 因为PVMediaScanner从MediaScanner中派生下来的,而且没有重载processDirectory函数
l&&&&&&&& Eclaire没有PVMediaScanner这样的东西,估计是froyo又改了点啥吧。
FT,processDirctory无非是列举一个目录内容,然后又反回去调用client的scanFile处理了。为何搞这么复杂?只有回去看看C&#43;&#43;的client干什么了。
3.&&&&&& MediaScannerClient---JNI层
JNI中的这个类是这样的:
class MyMediaScannerClient : public MediaScannerClient
//这是它的scanFile实现
virtual bool scanFile(const char* path, long long lastModified, long long fileSize)
//再次崩溃了,C&#43;&#43;的client调用了刚才传进去的java Client的
//scanFile函数…不过这次还传进去了该文件的路径,最后修改时间和文件大小。&&&&&
& mEnv-&CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize);
…..想死,,,
没办法了,只能再去看看MediaScanner.java传进去的那个client了。
4.&&&&&& MediaScannerClient----JAVA层
这个类在MediaScanner.java中实现。
private class MyMediaScannerClient implements MediaScannerClient:
public void scanFile(String path, long lastModified, long fileSize) {
//调用doScanFile..很烦..
&&&&&&&&&& doScanFile(path, null, lastModified, fileSize, false);
//下面是doScanFile
public Uri doScanFile(String path, String mimeType, long lastModified, long fileSize, boolean scanAlways) {
//预处理,看看之前创建的文件缓存中有没有这个文件
&& FileCacheEntry entry = beginFile(path, mimeType, lastModified, fileSize);
&&&&&&&&&&&&&&& // rescan for metadata if file was modified since last scan
&&&&&&&&&&&&&&& if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
//如果事先有这个文件的信息,则需要修改一些信息,如长度,最后修改时间等
&&&&&& …..
//真正的扫描文件
&&&&&&&&&&&&&&&&&&&&&&& processFile(path, mimeType, this);
//扫描完了,做最后处理
&&&&&&&&&&&&&&&&&&& endFile(entry, ringtones, notifications, alarms, music, podcasts);
processFile又是jni层的。
对应android_media_MediaScanner_processFile函数
android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client)
&&& MediaScanner *mp = (MediaScanner *)env-&GetIntField(thiz, fields.context);
& //无语了,又搞一个& MyMediaScannerClient
MyMediaScannerClient myClient(env, client);
&&& mp-&processFile(pathStr, mimeTypeStr, myClient);
第一次到PVMediaScanner中来了
status_t PVMediaScanner::processFile(const char *path, const char* mimeType, MediaScannerClient& client)
&&& status_
&&& InitializeForThread();
//调用client的beginFile,估计是做一些啥预处理
&&& client.setLocale(locale());
&&& client.beginFile();
&&& //LOGD(&processFile %s mimeType: %s/n&, path, mimeType);
&&& const char* extension = strrchr(path, '.');
&&& if (extension && strcasecmp(extension, &.mp3&) == 0) {
&&&&&&& result = parseMP3(path, client);//client又传进去了
…根据后缀名去扫描….
client.endFile();
到parseMP3去看看。这个在
static PVMFStatus parseMP3(const char *filename, MediaScannerClient& client)
{//这个函数太专业了,和编解码有关,我们重点关注client在这里干什么了
//原来,解析器从文件中解析出一个信息,就调用client的addStringTag
if (!client.addStringTag(&duration&, buffer))
addStringTag在JNI的client中处理。
这个MediaScannerClient是在opencore中的那个MediaScanner.cpp中实现的,而android_media_MediaScanner.cpp中的是MyMediaScannerClient,从MediaScannerClient派生下来的
&&& bool MediaScannerClient::addStringTag(const char* name, const char* value)
&&& if (mLocaleEncoding != kEncodingNone) {
&&&&&&& //字符串编码之类的转换。不详述了
&&&&&&& bool nonAscii =
&&&&&&& const char* chp =
&&&&&&& while ((ch = *chp&#43;&#43;)) {
&&&&&&&&&&& if (ch & 0x80) {
&&&&&&&&&&&&&&& nonAscii =
&&&&&&&&&&&&&&&
&&&&&&&&&&& }
//如果不是ASCII编码的话,内部先保存一下这些个tag信息
//待会扫描完后再集中做一次字符串编码转换
&&&&&&& if (nonAscii) {
&&&&&&&&&&& // save the strings for later so they can be used for native encoding detection
&&&&&&&&&&& mNames-&push_back(name);
&&&&&&&&&&& mValues-&push_back(value);
&&&&&&&&&&&
&&&&&&& // else fall through
//调用子类的handleStringTag
&&& return handleStringTag(name, value);
class MyMediaScannerClient : public MediaScannerClient{
//调用到子类的handleStringTag了
virtual bool handleStringTag(const char* name, const char* value)
//又传递到JAVA层的handleStringTag来处理
//麻木了..
&&& mEnv-&CallVoidMethod(mClient, mHandleStringTagMethodID, nameStr, valueStr);
MediaScannerService中的MyMediaScannerClient类
public void handleStringTag(String name, String value) {
//下层扫描的文件tag信息,全部处理后赋&#20540;给java层这个MyScannerClient了
例如MP3的title,专辑名等等。
&&&&&&&&&&&&&& ….
&&&&&&&&&&&&& int num = parseSubstring(value, 0, 0);
&&&&&&&&&&&&&&& mTrack = (num * 1000) &#43; (mTrack % 1000);
&&&&&&&&&&& } else if (name.equalsIgnoreCase(&duration&)) {
&&&&&&&&&&&&&&& mDuration = parseSubstring(value, 0, 0);
&&&&&&&&&&& } else if (name.equalsIgnoreCase(&writer&) || name.startsWith(&&)) {
&&&&&&&&&&&&&&& mWriter = value.trim();
到这里了,还没写到数据库呢?啥时候更新数据库?看来是在client.endFile()中了。
但是这个endClient并没有调用到JAVA层去。那在哪里结束呢?
还记得JAVA中的doScanFile函数吗,对了,这个endFile就是在那里直接由JAVA调用的。
private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications,
&&&&&&&&&&&&&&& boolean alarms, boolean music, boolean podcasts)
&&&&&&&&&&&&&&& throws RemoteException {
&&&&&&&&&&& // update database
&&&&&&&&&&& Uri tableU
&&&&&&&&&&& boolean isAudio = MediaFile.isAudioFileType(mFileType);
&&&&&&&&&&& boolean isVideo = MediaFile.isVideoFileType(mFileType);
&&&&&&&&&&& boolean isImage = MediaFile.isImageFileType(mFileType);
&&&&&&&&& ….
//来了一个新文件,直接插入数据库
&&&&&&&&&&&&&&& result = mMediaProvider.insert(tableUri, values);
//或者更新数据库
mMediaProvider.update(result, values, null, null);
这回真算是完了。
5.流程总结
l&&&&&&&& MediaScanner(MS)调用scanDirectories中的processDirectory,进入到JNI层
l&&&&&&&& JNI调用PVMediaScanner的processDirectory
l&&&&&&&& PVMediaScanner的processDirectory为目录下的文件调用MyMediaScannerClient的scanFile
l&&&&&&&& MyMediaScannerClient
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:252596次
积分:3750
积分:3750
排名:第5994名
原创:110篇
转载:49篇
评论:52条
欢迎加入 Android webkit 交流群
欢迎关注个人微信公众号
程序员互动联盟 微信号(coder_online)
专业,及时,互动,分享——这里是程序员梦想之家
(1)(23)(13)(3)(1)(15)(1)(2)(12)(4)(9)(1)(6)(16)(30)(5)(16)(18)

我要回帖

更多关于 winnner 的文章

 

随机推荐