delphi XE关于Android四大组件之ContentProvider:案例delphi XE加载Android手机图片的效率

delphi XE关于Android四大组件之ContentProvider:案例delphi XE加载Android手机图片的效率

delphi XE调用Android SDK方法开发App(一)运用Android四大组件

第一部分 原理篇

一、常用的单元引用

01、uses FMX.Helpers.Android;  //:Android线程与线程池、Android处理其系统内基本的表面绘制、Android数组转化

const
  /// <summary>Minimum base action number for using in Android Intent</summary>
  UserRequestCodeBase = 10;   //:请求Android的Intent,至少被调用的应用内置要有10个Action操作
  
function IsGingerbreadDevice: Boolean;  //:是否为姜黄饼干样的设备:指老掉牙的设备:TOSVersion.Major = 2;

//设置为主题皮肤:0-返回样式描述不成功-代表该窗体中没有样式本TStyleBook或有但未设置:用原生方法将Fmx的样式本设置为主题皮肤:
function GetNativeTheme: Integer; overload; 
function GetNativeTheme(const AControl: TControl): Integer; overload;   //:获取原生主题皮肤

{ 几个处理基本表面绘制的函数:Surface convertions }

function JBitmapToSurface(const SrcBitmap: JBitmap; const DestSurface: TBitmapSurface): Boolean;
function SurfaceToJBitmap(const SrcSurface: TBitmapSurface; const DestBitmap: JBitmap): Boolean;
function NativeViewToSurface(const ANativeView: JView; const ASurface: TBitmapSurface): Boolean;

{ 产生传入Java数组的转化函数:Java array conversion }

function CreateJavaStringArray(const ASource: array of const): TJavaObjectArray<JString>;

{ 用UI线程(即主线程)工作的内容:Work with UI Thread }

  TUIThreadCaller = class  //:线程调用类(匿名方法、异步调用与回调、强制出队)
  TFinishedRunnableCollector = class  //:处理已完成的线程(或任务)的列表容器类

var
  ActiveJavaRunnables: TThreadList<TRunnable>;  //:活动的任务的线程队列列表容器变量:uses System.Generics.Collections;  

 在UI线程中调用“回调事件或回调匿名方法”子线程的几个过程:线程入队-先进先出(非栈模式:进栈压入-后进先出-出栈弹出):

procedure CallInUIThread(AMethod: TMethodCallback); overload;   
procedure CallInUIThread(AMethod: TCallBack); overload;  //: UI线程直接执行被调用的子线程,但暂未释放,等待操作系统自动释放或等待引用窗体Activity被代码disposeof后释放,或UI线程(即主线程)被操作者关闭后自动释放
:异步非阻塞模式的队列线程池模式
procedure CallInUIThreadAndWaitFinishing(AMethod: TMethodCallback); overload;
procedure CallInUIThreadAndWaitFinishing(AMethod: TCallBack); overload;  //: UI线程等待被调用的线程执行完毕,
线程出队出栈弹出:异步非阻塞的队列线程池模式:uses System.SyncObjs; //:其中调用了TEvent事件总线监控:Event.SetEvent;  Event.WaitFor:子线程执行完毕立即释放

initialization
  ActiveJavaRunnables := TThreadList<TRunnable>.Create;  //:初始化:活动的任务的线程列表变量(初始化线程队列容器)  

finalization
  ActiveJavaRunnables.DisposeOf //:释放:活动的任务的线程列表变量(释放线程队列容器,并释放其中的未被释放的子线程-按队列先进先出)  
end.

02、uses Androidapi.JNI.Provider; //:Android的五大存储之四的内容提供者(ContentProvider)及内容接收器(ContentResolver)的处理单元

02.001、应用1:Android安卓系统缩略图内容提供者及内容接收器

  JImages_ThumbnailsClass = interface(JObjectClass)     
  {class} function getThumbnail(cr: JContentResolver; origId: Int64; kind: Integer; options: JBitmapFactory_Options): JBitmap; cdecl; overload;
    {class} function getThumbnail(cr: JContentResolver; origId: Int64; groupId: Int64; kind: Integer; options: JBitmapFactory_Options): JBitmap; cdecl; overload;
    {class} function query(cr: JContentResolver; uri: Jnet_Uri; projection: TJavaObjectArray<JString>): JCursor; cdecl;
    {class} function queryMiniThumbnail(cr: JContentResolver; origId: Int64; kind: Integer; projection: TJavaObjectArray<JString>): JCursor; cdecl;
    {class} function queryMiniThumbnails(cr: JContentResolver; uri: Jnet_Uri; kind: Integer; projection: TJavaObjectArray<JString>): JCursor; cdecl;  
  [JavaSignature('android/provider/MediaStore$Images$Thumbnails')]   //:内容提供者签名共享的URI路径内容
  JImages_Thumbnails = interface(JObject)
    ['{29CB9363-CD45-4C6E-86EF-FD421A2C7A11}']
  end;  
  TJImages_Thumbnails = class(TJavaGenericImport<JImages_ThumbnailsClass, JImages_Thumbnails>) end;

其中:thumbnail_cache路径是用Sqlite本地数据库缓存的,所以本质上是调用存取数据库的操作:

该数据库的表结构如下:

CREATE TABLE kv(k INTEGER PRIMARY KEY, v BLOB);   

       因而:介绍完原理之后,最后面就会作为一个案例分析:FMX调用该数据库进行缩略图列表和用Android原生方法列表缩略图的效率比较。

03、uses Androidapi.JNI.Media;  //Android媒体及其处理单元

03.001、应用1:缩略图工具类:

JThumbnailUtilsClass = interface(JObjectClass)
    ['{5D772E54-5912-4CF0-A97D-0A4B171E7EF7}']
    {class} function _GetOPTIONS_RECYCLE_INPUT: Integer; cdecl;  //:获取选项-回收再利用选项
    {class} function init: JThumbnailUtils; cdecl;  //:初始化实例化本工具类
    {class} function createVideoThumbnail(filePath: JString; kind: Integer): JBitmap; cdecl;  //:产生视频缩略图
    {class} function extractThumbnail(source: JBitmap; width: Integer; height: Integer): JBitmap; cdecl; overload;
    {class} function extractThumbnail(source: JBitmap; width: Integer; height: Integer; options: Integer): JBitmap; cdecl; overload;  //:提取缩略图
    {class} property OPTIONS_RECYCLE_INPUT: Integer read _GetOPTIONS_RECYCLE_INPUT;//:回收再利用选项属性
  end;

  [JavaSignature('android/media/ThumbnailUtils')]
  JThumbnailUtils = interface(JObject)
    ['{EF230179-DF54-4876-A9BC-5D982DF95E21}']
  end;
  TJThumbnailUtils = class(TJavaGenericImport<JThumbnailUtilsClass, JThumbnailUtils>) end;

第二部分 案例篇

直接上代码(内附详细说明):

 

unit MasterDetail;

interface

uses
  System.SysUtils, System.Types, System.UITypes,
  System.Classes, System.Variants,

  System.Rtti, System.Bindings.Outputs, System.Actions,
  {$IFDEF Android}

  FMX.Helpers.Android,
  Androidapi.IOUtils, Androidapi.Helpers,
  Androidapi.JNI.Provider,
  Androidapi.JNIBridge, Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Net,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Media,

  {$ENDIF Android}
  Data.Bind.GenData, Data.Bind.Components, Data.Bind.ObjectScope,

  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics,
  FMX.Dialogs, FMX.ListView.Types,

  Fmx.Bind.GenData,

  Fmx.Bind.Editors, Data.Bind.EngExt,
  Fmx.Bind.DBEngExt,

  FMX.Objects, FMX.StdCtrls,
  FMX.ListView, FMX.ListView.Appearances,
  FMX.Layouts, FMX.MultiView,FMX.Memo, Fmx.Bind.Navigator,
  FMX.ActnList,
  FMX.ListView.Adapters.Base, FMX.ScrollBox, FMX.Controls.Presentation;

type
  TMasterDetailForm = class(TForm)
    MultiView1: TMultiView;
    Layout1: TLayout;
    ListView1: TListView;
    MasterToolbar: TToolBar;
    MasterLabel: TLabel;
    DetailToolbar: TToolBar;
    DetailLabel: TLabel;
    MasterButton: TSpeedButton;
    imgContact: TImage;
    lblName: TLabel;
    lblTitle: TLabel;

    ActionList1: TActionList;

    PrototypeBindSource1: TPrototypeBindSource;
    BindingsList1: TBindingsList;
    LinkPropertyToFieldBitmap: TLinkPropertyToField;
    LinkPropertyToFieldText: TLinkPropertyToField;
    LinkPropertyToFieldText2: TLinkPropertyToField;

    LinkControlToField1: TLinkControlToField;
    LinkListControlToField1: TLinkListControlToField;
    LiveBindingsBindNavigateNext1: TFMXBindNavigateNext;
    LiveBindingsBindNavigatePrior1: TFMXBindNavigatePrior;

    Layout2: TLayout;
    Layout3: TLayout;
    Memo1: TMemo;
    btnUp: TSpeedButton;
    btnDown: TSpeedButton;
    MetropolisUIBlue_touch: TStyleBook;//:加个你的FMX样式本控件

    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure MultiView1StartShowing(Sender: TObject);

    procedure ListView1UpdateObjects(const Sender: TObject;
      const AItem: TListViewItem);
    procedure ListView1UpdatingObjects(const Sender: TObject;
      const AItem: TListViewItem; var AHandled: Boolean);

    procedure ListView1ScrollViewChange(Sender: TObject);

    procedure ListView1ItemClick(const Sender: TObject; const AItem: TListViewItem);
    procedure MasterLabelClick(Sender: TObject);

  private
    FPicPath:string;
    FImgsStringList:TStringList;
    FDefaultRecordCound_PrototypeBindSource:Integer;
    FUpdatingItemView:Boolean;//:ItemView是否正在更新或更新已完毕
    procedure FetchAndroidSysdada(URI_ContentProvider:Jnet_Uri);
    { Private declarations }
  public
    { Public declarations }
  end;
var
  MasterDetailForm: TMasterDetailForm;

  //本案注释部分和lblTitle即Memo1的赋值主要用于调试:
implementation
{$R *.fmx}

uses
  myFuc_UnifiedPlatForm, myFuc_Client;
    //:这是我自己用的库单元,主要用于本例需要使用的权限和下面的路径生成(路径本例可不用)

procedure TMasterDetailForm.FormDestroy(Sender: TObject);
begin
  if Assigned(FImgsStringList) then
    FImgsStringList.Free;
end;

procedure TMasterDetailForm.FormCreate(Sender: TObject);
begin
  try
    AndoidRequestPermissions(['管理文档','读取文件','写入文件']);//:合称"存储权限"
    //:你需要实现Android运行时的动态权限申请和用户确认
  finally
    try
      SubPathOfAppPublished; //:路径生成(路径本例可不用)
    finally
      FPicPath:=GetSubPathOfAppPublished; //:路径获取(路径本例可不用)
    end;
    {$IFDEF Android}
      //ShowMessage(GetNativeTheme(MultiView1).ToString);//:0-返回样式描述不成功-代表该窗体中没有样式本TStyleBook或有但未设置
      //ShowMessage(GetSharedPicturesDir+'/'+'thumbnail_cache');
    {$ENDIF Android}
  end;

end;

procedure TMasterDetailForm.ListView1ItemClick(const Sender: TObject; const AItem: TListViewItem);
begin
  MultiView1.HideMaster;
end;

procedure TMasterDetailForm.ListView1ScrollViewChange(Sender: TObject);
var
  LItem: TListViewItem;

  LTableStruct_SplitStrArray:TArray<string>; //:缩略图表结构字段数值字符串分割数组:长度为:6:下面共6个字段
  LFilePath_NameArray:TArray<string>;        //:缩略图文件存取路径字符串分割数组
  L_id,L_data,Limage_id,Lkind,Lwidth,Lheight:string; //:缩略图表结构字段数值变量:共6个字段
begin   //PrototypeBindSource1
  //if FDefaultRecordCound_PrototypeBindSource < FImgsStringList.Count then
  if ListView1.ItemCount < FImgsStringList.Count then
  begin
    LItem:=ListView1.Items.Add;
      INC(FDefaultRecordCound_PrototypeBindSource);

    LTableStruct_SplitStrArray:=FImgsStringList[LItem.Index].Split([',']);

    L_id:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-6];//:即LTableStruct_SplitStrArray[0]
    L_data:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-5];//:即LTableStruct_SplitStrArray[1]

    Limage_id:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-4];//:即LTableStruct_SplitStrArray[2]
    Lkind:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-3];//:即LTableStruct_SplitStrArray[3]

    Lwidth:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-2];//:即LTableStruct_SplitStrArray[4]
    Lheight:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-1];//:即LTableStruct_SplitStrArray[5]


    LFilePath_NameArray:=L_data.Trim.Split(['/']);
    LItem.Text:=LFilePath_NameArray[Length(LFilePath_NameArray)-1];

    //lblTitle.Text:=' 索引: '+LItem.Index.ToString +' ,存取路径: '+L_data.Trim
      //+' ,当前加载图片数Items.Count: '+ListView1.Items.Count.ToString.Trim;
      //+' ,ItemCount: '+ListView1.ItemCount.ToString.Trim
    //:200默认PrototypeBindSource1每页记录数
  end;
  //LTableStruct_SplitStr:=
    //L_id.ToString+','+L_data.Trim+','+Limage_id.ToString+','
    //+Lkind.ToString+','+Lwidth.ToString+','+Lheight.ToString;
end;

procedure TMasterDetailForm.ListView1UpdateObjects(
  const Sender: TObject;
  const AItem: TListViewItem);
var
  LListItemImage:TListItemImage;
  LTableStruct_SplitStrArray:TArray<string>; //:缩略图表结构字段数值字符串分割数组:长度为:6:下面共6个字段
  LFilePath_NameArray:TArray<string>;        //:缩略图文件存取路径字符串分割数组
  L_id,L_data,Limage_id,Lkind,Lwidth,Lheight:string; //:缩略图表结构字段数值变量:共6个字段
begin
  if (AItem<>nil) and (AItem.View.DrawableByName('I')<>nil) then
  begin
      //lblTitle.Text:='找到了:'+AItem.View.DrawableByName('I').Name;
    LTableStruct_SplitStrArray:=FImgsStringList[AItem.Index].Split([',']);

    L_id:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-6];//:即LTableStruct_SplitStrArray[0]
    L_data:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-5];//:即LTableStruct_SplitStrArray[1]

    Limage_id:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-4];//:即LTableStruct_SplitStrArray[2]
    Lkind:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-3];//:即LTableStruct_SplitStrArray[3]

    Lwidth:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-2];//:即LTableStruct_SplitStrArray[4]
    Lheight:= LTableStruct_SplitStrArray[Length(LTableStruct_SplitStrArray)-1];//:即LTableStruct_SplitStrArray[5]


    LFilePath_NameArray:=L_data.Trim.Split(['/']);
    AItem.Text:=LFilePath_NameArray[Length(LFilePath_NameArray)-1];

    AItem.BeginUpdate;
      LListItemImage:=(AItem.View.DrawableByName('I') as TListItemImage);
      LListItemImage
        .Bitmap.LoadThumbnailFromFile(
          L_data,//FImgsStringList[AItem.Index].Trim,
          LListItemImage.Width*(Lwidth.ToInteger/Lheight.ToInteger),
          LListItemImage.Height*(Lwidth.ToInteger/Lheight.ToInteger),
          true);
      //if Lheight.ToInteger>Lwidth.ToInteger then LListItemImage.Bitmap.Rotate(90);//:控制旋转
    AItem.EndUpdate;
  end;
  lblTitle.Text:='索引:'+AItem.Index.ToString +',存取路径: '+L_data.Trim
    +' ,当前加载图片数Items.Count: '+ListView1.Items.Count.ToString.Trim;
    //+' ,ItemCount: '+ListView1.ItemCount.ToString.Trim
  //:200默认PrototypeBindSource1每页记录数
  //LTableStruct_SplitStr:=
    //L_id.ToString+','+L_data.Trim+','+Limage_id.ToString+','
    //+Lkind.ToString+','+Lwidth.ToString+','+Lheight.ToString;
  FUpdatingItemView:= false;//:ItemView是否正在更新或更新已完毕
end;

procedure TMasterDetailForm.ListView1UpdatingObjects(const Sender: TObject;
  const AItem: TListViewItem; var AHandled: Boolean);
begin
  FUpdatingItemView:= true;//:ItemView是否正在更新或更新已完毕
end;

procedure TMasterDetailForm.MasterLabelClick(Sender: TObject);
var LCurrentTotalItemsCount: Integer;
begin
  //if FUpdatingItemView= true then exit;//:ItemView是否正在更新或更新已完毕
  try
    LCurrentTotalItemsCount:=ListView1.Items.Count;
    //if ListView1.ScrollViewPos then

  finally
    if MasterLabel.Text.Trim='到开头' then
    begin
      ListView1.ScrollTo(0);
      MasterLabel.Text:='到当前已加载图片的末尾';
    end else
    begin
      //:设置ListView的当前索引:ListView1.ItemIndex:=ListView1.ItemCount; //ListView1.Items.AppearanceItem[ListView1.ItemCount];
      try
        ListView1.ScrollTo(LCurrentTotalItemsCount-1);
      finally
        MasterLabel.Text:='到开头';
      end;
    end;
  end;

end;

procedure TMasterDetailForm.MultiView1StartShowing(Sender: TObject);
var URI_ContentProvider:Jnet_Uri;
begin
  //:调用ContentResolver提取ContentProvider的数据//uses Androidapi.JNI.Provider
  CallInUIThreadAndWaitFinishing(
  procedure
  begin
    URI_ContentProvider:=StrToJURI('content://media/external/images/thumbnails');//:uses Androidapi.Helpers;
    FetchAndroidSysdada(URI_ContentProvider);
      //:如果你不知道正确的URI,请FetchAndroidSysdada(nil);
  end);
  //:这些都是错误的URI:
  //content://android.providers.media/thumbnail_cache/thumbnail.db/kv
  //content://android.provider.MediaStore$Images$Thumbnails
  //MediaStore.Audio.Images.INTERNAL_CONTENT_URI
end;

procedure TMasterDetailForm.FetchAndroidSysdada(URI_ContentProvider:Jnet_Uri);
var
  LContext: JContext;
  LJContentResolver: JContentResolver;
  Lprojection: TJavaObjectArray<JString>; LJavaBasicArray:TJavaBasicArray;
  LJCursor: JCursor;
  LRecCount,
  LRecIndex: Integer;
  LColumnCounts,
  LColumnFieldType: Integer;
  LColumnName: string;
  LImgBlobTBytes:TJavaArray<Byte>;
  LImageIndex:Integer;
  LImagePathInStringList: string;
  LIFImageDuplicates:Boolean;
  LTableStruct_SplitStr: string; //:用于存取下属表结构字段数值并用,分割后,写入全局列表FImgsStringList
  //以下几个变量一一对应表中的各字段(去变量前置L字符即是):
  L_id: Integer;
  L_data: string;
  Limage_id: Integer;
  Lkind: Integer;
  Lwidth: Integer;
  Lheight: Integer;

begin
//参考:FMX.AddressBook.Android:
//procedure TAndroidAddressBook.FetchContacts(var AContacts: TAddressBookContacts; const AFilter: string = '');

  LJavaBasicArray:=TJavaBasicArray.Create;
  //Lprojection:= CreateJavaStringArray(['kv.k','kv.v']);//TJavaObjectArray<JString>.Wrap(LJavaBasicArray);
  Lprojection:= CreateJavaStringArray(['*']);//TJavaObjectArray<JString>.Wrap(LJavaBasicArray);
    //:'*':  select * 字段列表
    //LJContentResolver:=TJContentResolver.JavaClass.init(LContext); //: 抽象内容
    //URI_ContentProvider:=TJImages_Thumbnails.JavaClass.getContentUri(StringToJString(GetSharedPicturesDir+'/thumbnail_cache'));
      //: 抽象内容
  if URI_ContentProvider=nil then
    URI_ContentProvider:=TJImages_Thumbnails.JavaClass.EXTERNAL_CONTENT_URI;
    //:URI不能为nil:不能使用抽象内容的提供者方法:如果访问外部内容提供者的内容(用户的数据)要有权限
  //Memo1.Lines.Clear;
  //Memo1.Lines.Add(JStringToString(TJImages_Thumbnails.JavaClass.INTERNAL_CONTENT_URI.toString));
    //:INTERNAL_CONTENT_URI:内部数据-系统只对外提供表结构,记录数始终=0
    //URI数值:content://media/internal/images/thumbnails
  //Memo1.Lines.Add(JStringToString(TJImages_Thumbnails.JavaClass.EXTERNAL_CONTENT_URI.toString));
    //:EXTERNAL_CONTENT_URI:用户的数据(外部内容提供者的内容):需要READ_EXTERNAL_STORAGE权限或grantUriPermission();
    //URI数值:content://media/external/images/thumbnails
  //Memo1.Lines.Add(JStringToString(TJImages_Thumbnails.JavaClass.THUMB_DATA));//:返回'thumb_data'没起什么作用

  LJCursor:=TAndroidHelper.ContentResolver
  .query(
    URI_ContentProvider,//TJImages_Thumbnails.JavaClass.INTERNAL_CONTENT_URI,//:查询的表名
    Lprojection, //:查询的列数组
    StringToJString(string.Empty), //:查询的where条件
    nil,                           //:查询参数数组:where中的占位符提供具体的值
    StringToJString('_data desc')         //:查询的排序字段的字符串列表','分割:_data根据下面字段列表反推得到
      //_data:被分割提取后的文件名:就是时间截13位的其中后3位为毫秒:可用于排序和时间分类,就和手机自带的【图库】一样:
        //LFilePath_NameArray:=FImgsStringList[LItem.Index].Split(['/']);
        //LItem.Text:=LFilePath_NameArray[Length(LFilePath_NameArray)-1];
  );

  {LJCursor:=TJImages_Thumbnails.JavaClass
  .queryMiniThumbnail(
    LJContentResolver,
    0,
    1,
    Lprojection
  ); //}

  //function query(uri: Jnet_Uri; projection: TJavaObjectArray<JString>; selection: JString; selectionArgs: TJavaObjectArray<JString>; sortOrder: JString): JCursor;
  {对应参数的解释://https://blog.csdn.net/xianKOG/article/details/81702119?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522158731319919724846464763%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=158731319919724846464763&biz_id=0&utm_source=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-4
  query()方法的参数:相当于本地sqlite缓存数据库下SQL部分        描述:
  uri	               from table_name	                          指定查询某个应用程序下的某个表
  projection	       select column1, column2	                  指定查询的列名
  selection	         where column=value	                        指定where约束条件
  selectArgs	                                                  为where中的占位符提供具体的值
  orderBy	           order by column1, column2	                指定查询结果的排序方式}
  LRecCount:=LJCursor.getCount;
  //Memo1.Lines.Add('表中记录数:'+LRecCount.ToString);
  if LRecCount>0 then //:如果表存在且有记录:Sqlite:始终有1条记录,哪怕表记录为空
  begin              //LJCursor.getColumnCount
    LJCursor.moveToFirst; //:如果不给这一句:游标的索引越界
    LColumnCounts:=0;
    while LColumnCounts<LJCursor.getColumnCount do
    begin
      LColumnName:=JStringToString(LJCursor.getColumnName(LColumnCounts));
      LColumnFieldType:= LJCursor.getType(LColumnCounts);
      //Memo1.Lines.Add('表列名('+LColumnCounts.ToString+'):'+LColumnName+',列字段类型索引:'+LColumnFieldType.ToString);
      INC(LColumnCounts);
    end;
    //:获取的表结构如下://URI_ContentProvider=TJImages_Thumbnails.JavaClass.INTERNAL_CONTENT_URI
      //表中记录数:0
      //表列名(0):_id
      //表列名(1):_data
      //表列名(2):image_id
      //表列名(3):kind
      //表列名(4):width
      //表列名(5):height
    //:获取记录总数及表结构如下://URI_ContentProvider=TJImages_Thumbnails.JavaClass.EXTERNAL_CONTENT_URI
      //表中记录数:5353
      //表列名(0):_id,列字段类型索引:1       (1:整数类型)   字段含义:缩略图表_id号,与Sqlite自增长字段相关
      //表列名(1):_data,列字段类型索引:3     (3:字符串类型) 字段含义:缩略图的手机存取路径
      //表列名(2):image_id,列字段类型索引:1                 字段含义:缩略图编码关联字段,默认=_id
      //表列名(3):kind,列字段类型索引:1                     字段含义:缩略图来源类型:比如图片媒体:1(默认),视频媒体2(未去验证):实际未怎么使用
      //表列名(4):width,列字段类型索引:1                    字段含义:缩略图的宽度像素
      //表列名(5):height,列字段类型索引:1                   字段含义:缩略图的高度像素
    LJCursor.moveToFirst; LRecIndex:=1;
    LJCursor.getWantsAllOnMoveCalls;
    if not Assigned(FImgsStringList) then
      FImgsStringList:=TStringList.Create;
    FImgsStringList.Clear;
    //Memo1.Lines.Add('各记录数值如下:');
    //while LJCursor.moveToNext do
    for LRecIndex := 0 to LRecCount-1 do
    begin  //:开始提取缩略图列的数据:getBlob(columnIndex: Integer): TJavaArray<Byte>;
      //if not LJCursor.isNull(LJCursor.getColumnIndex(StringToJString('_data'))) then
      //if not LJCursor.isNull(3) then
      begin
        {LImgBlobTBytes:=LJCursor.getBlob(1);
        Memo1.Lines.Add(
          '_id:'+LJCursor.getInt(0).ToString+','
          +'image_id:'+LJCursor.getInt(2).ToString+','
          +'kind:'+LJCursor.getInt(3).ToString+','
          +'width:'+LJCursor.getInt(4).ToString+','
          +'height:'+LJCursor.getInt(5).ToString+','
          +'_data:'+JStringToString(LJCursor.getString(1))+','
          +'_data的字节长度:'+LImgBlobTBytes.Length.ToString
        ); }
        //各记录数值如下:
        //_id:1,  image_id:7604,  kind:1,  width:480,  height:270,
        //_data:/storage/emulated/0/DCIM/.thumbnails/1569643422581.jpg,  _data的字节长度:55
        //_data:被分割提取后的文件名:就是时间截13位的其中后3位为毫秒:可用于排序和时间分类,就和手机自带的【图库】一样:
          //LFilePath_NameArray:=FImgsStringList[LItem.Index].Split(['/']);
          //LItem.Text:=LFilePath_NameArray[Length(LFilePath_NameArray)-1];
        //INC(LRecIndex);
        L_id:=LJCursor.getInt(0);
        L_data:=JStringToString(LJCursor.getString(1));
        Limage_id:=LJCursor.getInt(2);
        Lkind:=LJCursor.getInt(3);
        Lwidth:=LJCursor.getInt(4);
        Lheight:=LJCursor.getInt(5);

        LTableStruct_SplitStr:=
          L_id.ToString+','+L_data.Trim+','+Limage_id.ToString+','
          +Lkind.ToString+','+Lwidth.ToString+','+Lheight.ToString;
        //LIFImageDuplicates:=false;
        try
          {for LImagePathInStringList in FImgsStringList do
          begin
            if LImagePathInStringList.Trim=L_data.Trim then
              LIFImageDuplicates:=true;
          end; //:循环会大大降低执行效率并阻塞界面}
          //LIFImageDuplicates:=FImgsStringList.Find(L_data,LImageIndex )
            //:.Find :在子线程中不生效,只能在UI线程
          //:只能在循环外,每次都重新查询后赋值1次:FImgsStringList.Clear
        finally
          //if LIFImageDuplicates=false then
            //FImgsStringList.Add(L_data);   //:LImageIndex
            FImgsStringList.Insert(LRecIndex,LTableStruct_SplitStr);
        end;
        if not LJCursor.isLast then LJCursor.moveToNext;
      end;
    end; //}
    if FDefaultRecordCound_PrototypeBindSource<1 then
      FDefaultRecordCound_PrototypeBindSource:=200;
    Memo1.Lines.Clear;
    Memo1.Lines.Add( '缩略图总数: '+FImgsStringList.Count.ToString
      +' ,默认每页记录数(200),当前从此记录开始加载: '+FDefaultRecordCound_PrototypeBindSource.ToString );
      //:循环后记录号:5353,与记录总数吻合
    //imgContact.Bitmap.LoadFromFile(JStringToString(LJCursor.getString(1)));
      //:测试UI加载路径图片
    LJCursor.close;
  end;


end;

end.

 

附注:转好文章:《Android之四大组件、六大布局、五大存储》

https://blog.csdn.net/shenggaofei/article/details/52450668?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-13&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-13

续:看看Android四大组件之ContentProvider的内容提供者,我们还有哪些可在delphiXE中运用:

https://blog.csdn.net/pulledup/article/details/105699037

喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:

 

©️2020 CSDN 皮肤主题: Age of Ai 设计师: meimeiellie 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值