19/5-6:
请简述 LinkedHashMap 的工作原理和使用方式?
LinkedHashMap 是继承自 HashMap 实现Map接口,
LinkedHashMap 和 HashMap 的主要区别就是 LinkedHashMap 是有序的,而 HashMap 是无序的。
LinkedHashMap 默认为插入的顺序,他是基于HashMap 和 双向链表实现的,LinkedHashMap 是线程不安全的。
AsyncTask 启动的方式?
在SDK3.0以前的版本执行 asyncTask.execute(task) 时的确是多线程并发执行的,线程池大小为5,最大128个,google 在3.0以后做了修改 ,将 asyncTask.execute(task)修改为了顺序执行,即只有当一个任务完成后才执行下一个任务
那么怎么并发执行呢?很简单,3.0后新增了一个方法 executeOnExecutor(Executor exec ,Object… params),该方法接收两个参数,第一个是 Executor ,第二个是任务参数,第一个是线程池实例,google 为我们定义了两种,第一种是AsyncTask.SERIAL_EXECUTOR ,第二种是AsyncTask.THREAD_POOL_EXECUTOR,顾名思义,第一种就是3.0 以后的方法,是顺序执行的,第二种就是3.0以前的 execute 方法,是可以并发执行的,我们直接用asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, task);就可以多任务并发执行了。
自定义Executor
private static ExecutorService exec = Executors.newSingleThreadExecutor(); //程序在每次调用时是使用的同一个Executor, syncTask.executeOnExecutor(exec, task); //当Executor类型为下面时 只有两个线程在执行任务 private static ExecutorService exec = Executors.newFixedThreadPool(2); syncTask.executeOnExecutor(exec, task);
19/5-7
谈一谈JAVA 的垃圾回收机制?
垃圾回收机制:
当堆内存中的某块区域没有对象引用时,这个内存区域就会变成垃圾,等待垃圾回收器的回收,
怎么找到无用对象:
引用计数:最简单的寻找无用对象的机制,当一个对象被引用一次,引用计数+1,当失去引用时引用计数-1,当此对象引用计数为0时可以直接回收。这种方法有一个显而易见的问题:无法回收被循环引用的对象。
可达性分析:从一个根对象(GC Root)开始向下搜寻,可以认为搜寻到的所有有强引用的对象都是活跃对象,所有找不到的对象都是无用对象,无用对象可能会被即刻回收,也可能进行其他操作(比如执行对象的finalize()方法)
这里还会引出一点问题:关于强引用,软引用,弱引用和虚引用的分别处理,具体可以看
强制垃圾回收:
程序只能控制一个对象不被任何引用变量引用,绝对不能控制它的回收。
System.gc();
Runntime.getTuntime().gc();
上面两个方法会建议系统进行垃圾回收,但是系统也有可能不进行回收。
对象的复活可以通过 finalize()方法来实现,
android ButterKnife 依赖
1,如果项目只有一个 主module ,则依赖添加如下:
在module 的build.gradle 中添加如下
api 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
在项目的 build.gradle 中添加:
dependencies { //noinspection GradleDependency classpath 'com.android.tools.build:gradle:3.2.0' //ButterKnife 依赖 classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0' }
2,如果在library中使用,则需要给library的 build.gradle 中添加如下:
apply plugin: 'com.android.library' api 'com.jakewharton:butterknife:8.4.0' //noinspection GradleDependency annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
在项目的 build.gradle 中添加:
dependencies { //noinspection GradleDependency classpath 'com.android.tools.build:gradle:3.2.0' //ButterKnife 依赖 classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0' }
使用注意:
第一步为 绑定ButterKnife,
如果是一个 主module 中使用,则可以直接使用R来调用资源文件,但是如果在 library 中使用,则需要使用R2 来调用资源文件.
设置一个单选对话框:
private String[] mGenders = new String[]{"男","女","保密"}; getGenderDialog(new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { final AppCompatTextView textView = view.findViewById(R.id.tv_arrow_value); textView.setText(mGenders[which]); } }); //设置一个单选对话框 private void getGenderDialog(DialogInterface.OnClickListener listener){ final AlertDialog.Builder builder = new AlertDialog.Builder(DELEGATE.getContext()); builder.setSingleChoiceItems(mGenders,0,listener); builder.show(); }
19/5-9
类加载过程
类加载的过程分为三个部分:
1,加载 类加载是将.class 文件中的二进制数据读入内存中,将其放在运行时数据区的方法区内,然后在堆内存中创建一个对象,又来封装类在方法区内的数据接口。类加载的最终产品是为与堆中的Class 对象。
2,类的连接 类加载完成后,就会进入连接阶段,连接阶段负责把类的二进制数据合并到jre中,可分为三个阶段
1,验证。
2,准备。
3,解析。
3,类的初始化 在类的初始化阶段,虚拟机负责对类进行初始化,主要是对类变量进行初始化。JVM初始化一个类包含如下几个步骤
1,假如这个类没有被加载和连接,则程序加载并连接该类。
2,假如该类的直接父类还没有被初始化,则先初始化其直接父类。
3,假如类中有初始化语句,则系统一次执行这些初始化语句。
谈谈如果优化ListView
1,在适配器中尽量少使用逻辑
不要在getView()中写过多的逻辑代码。
2,GC垃圾回收器
当创建了大量的对象时,GC就会频繁的运行,所以在getView() 方法中不要创建非常多的对象。假设你的 log 里 面发现“GC has freed dome memory”频繁出现,那么程序肯定有问题了。
你可以检查一下:
1,item 布局的层级是否太深。
2,getView()方法是否有大量对象存在。
3,ListView 的布局属性。
3,载入图片
如果你的ListView 必须要从网络上下载图片,我们不要在 ListView 滑动的时候加载图片,那样会让ListView 变得卡顿,所以我们要监听ListView 的状态,在滑动的时候 停止载入图片,没有滑动在 开始载入图片。
listView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView listView, int scrollState) { //停止载入图片 if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { imageLoader.stopProcessingQueue(); } else { //開始载入图片 imageLoader.startProcessingQueue(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub } });
4,将 ListView 的 scrollingCache 和 animateCache 设置为false
scrollingCache: scrollingCache本质上是drawing cache,你能够让一个View将他自己的drawing保存在cache中(保存为一个bitmap),这样下次再显示View的时候就不用重画了,而是从cache中取出。默认情况下drawing cahce是禁用的。由于它太耗内存了,可是它确实比重画来的更加平滑。
而在ListView中,scrollingCache是默认开启的,我们能够手动将它关闭。
animateCache: ListView 默认开启了animateCache,这会消耗大量的内存,因此会频繁调用GC,我们能够手动将它关闭掉。
5,降低item的布局的深度
5,使用ViewHolder。
再次建议使用RecyclerView。
日期选择器 DatePicker 的封装
public class DateDialogUtil { public interface IDateListener{ void onDateChange(String date); } private String data = null; private IDateListener mDataListener = null; public void setDateListener(IDateListener listener){ this.mDataListener = listener; } public void showDialog(final Context context){ final LinearLayout ll = new LinearLayout(context); final DatePicker picker = new DatePicker(context); final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT); picker.setLayoutParams(lp); picker.init(1990, 1, 1, new DatePicker.OnDateChangedListener() { @Override public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { final Calendar calendar = Calendar.getInstance(); //设置时间 calendar.set(year,monthOfYear,dayOfMonth); //日期格式化,使用此Java虚拟机实例的默认区域设置的当前值 SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日",Locale.getDefault()); data = format.format(calendar.getTime()); } }); ll.addView(picker); new AlertDialog.Builder(context) .setView(ll) .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (mDataListener != null && data != null){ mDataListener.onDateChange(data); } } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .show(); } }
final DateDialogUtil dateDialogUtil = new DateDialogUtil(); dateDialogUtil.setDateListener(new DateDialogUtil.IDateListener() { @Override public void onDateChange(String date) { final TextView textView = view.findViewById(R.id.tv_arrow_value); textView.setText(date); } });
19/5-10
谈谈你对 Activity.runOnUiThread 的理解?
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
判断当前是不是 ui 线程,如果是就直接运行,否则就通过handler的post方法切换到主线程
Android 权限的封装
权限的封装
注解权限:PermissionsDispatcher 的简单使用:
@RuntimePermissions :这是必须使用的注解,用于标注你想要的申请权限的Activity或者Fragment,
@RuntimePermissions public abstract class PermissionCheckerDelegate extends Activity { }
@NeedsPermission(Manifest.permission.CAMERA) :
这个也是必须要使用的注解, 用于标注你需要获取权限的方法,注解括号里面有个参数,传入想要申请得权限,可以传入多个。当获得了对应的权限后就会执行这个方法。
@NeedsPermission(Manifest.permission.CAMERA) void startCamera() { LatteCamera.start(this); }
@OnShowRationale(Manifest.permission.CAMERA) :
这个不是必须的注解,用于标注申请权限 时需要执行的方法,传入想要申请的权限,还需要一个PermissionRequest对象,这个对象有两种方法 proceed()让权限继续请求,canncel()让请求中断。也就是说,这个方法会拦截你发出的请求,这个方法* 用于告诉你接下来申请权限是干啥的,说服用户给你权限
@OnShowRationale(Manifest.permission.CAMERA) void onCameraReational(PermissionRequest request) { showRetionaeDialog(request); } private void showRetionaeDialog(final PermissionRequest request) { new AlertDialog.Builder(getContext()) .setPositiveButton("同意使用", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { request.proceed(); } }) .setNegativeButton("拒绝使用", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { request.cancel(); } }) .setCancelable(false) .setMessage("权限管理") .show(); }
@OnPermissionDenied注解:
这个也不是必须的注解,用于标注如果权限请求失败, 但是用户没有勾选不再询问的时候执行的方法,注解括号里面有参数,传入想要申请的权限。 也就是说,我们可以在这个方法做申请权限失败之后的处理,如像用户解释为什么要申请,或者重新申请操作等。
onRequestPermissionsResult()方法
Rebuild 之后会生成一个辅助类,用来调用被注解的Activity的方法,所以,第一次使用的话* 注解添加完后 要Rebuild 一次。否则不能生成辅助类。
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] data) { super.onRequestPermissionsResult(requestCode, permissions, data); PermissionCheckerDelegatePermissionsDispatcher .onRequestPermissionsResult(this, requestCode, data); }
使用如下
//调用该方法后就会申请权限,成功之后就会执行相应的代码 public void startCameraWithCheck() { PermissionCheckerDelegatePermissionsDispatcher.startCameraWithPermissionCheck(this); }
封装一个全局的回调接口:
public interface IGlobalCallback<T> { void executeCallBack(T args); }
public enum CallBackType { /** * 剪裁后的回调 */ ON_CROP }
public class CallbackManager { private static final WeakHashMap<Object,IGlobalCallback> CALLBACKS = new WeakHashMap<>(); private static class Holder{ private static final CallbackManager IINSTANCE = new CallbackManager(); } public static CallbackManager getInstance(){ return Holder.IINSTANCE; } public CallbackManager addCallback(Object tag ,IGlobalCallback callback ){ CALLBACKS.put(tag,callback); return this; } public IGlobalCallback getCallBack(Object tag){ return CALLBACKS.get(tag); } }
//使用如下: //实现 CallbackManager.getInstance() .addCallback(CallBackType.ON_CROP, new IGlobalCallback<Uri>(){ @Override public void executeCallBack(Uri args) { Log.e(TAG, "executeCallBack: "+args ); } }); //调用 final IGlobalCallback<Uri> callback = CallbackManager .getInstance() .getCallBack(CallBackType.ON_CROP); if (callback != null){ callback.executeCallBack(cropUri); }
调用手机相册,照相机,裁剪图片
点击进行查看
19/5-11
请列出几种常见的工厂模式,并说明他的用法?
工厂模式
问题:打开项目后文件名字都是J 开头,全部报红,xml 文件打开后乱码。解决办法:
经过查看,使用notepad 打开后发现一切正常,最后将项目换个位置。打开后一切正常
19/5-12
请说一下HashMap 和 HashTable 的区别?
1,HashMap 支持 null 键 和null值,而HashMap 在遇到null 是,会抛出 空指针异常。
2,我们说HashTable是同步的,HashMap不是,也就是说HashTable在多线程使用的情况下,不需要做额外的同步
3, HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。
4,HashTable 继承子Dictionary 类,二HashMap 继承AbstractMap 类,但是二者都实现了Map 接口
5, Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
6,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
7,HashTable已经被淘汰了,不要在代码中再使用它。
针对RecyclerView 做了哪些优化
1,onBindViewHolder
这个方法的含义 就是绑定数据,并且是在UI线程。所以尽量在这个方法中少做一些业务的处理 。
2,数据优化采用
android 支持包晓得DiffUtil 集合工具类结合RV分页加载会更友好,节省性能
3,item 优化
减少项目的视图层级,如果项目的高度可以固定的话 可以设置setHasFixedSize(真),避免requestLayout 浪费资
源
4,使用RecyclerViewPool
RecyclerViewPoll 是对项目进行缓存的。项
5,刷新时 可以使用局部刷新
mAdapter.notifyItemRangeChanged()
19/5-13
“equals” 与 “==” ,“hashCode”的区别和使用
一般情况下 equals比较的是 对象的地址,和 == 一样,但是有很多类重写了equals 方法。比如String 等。
而 hashcode( ) 方法返回一个 int 数,在object 类中的默认实现是 “将该对象的内存地址 转换成一个整数返回”
hashCode 的 常规协定
在java 程序执行期间,在同一对象上调用多次 hashCode 返回的 int 数 必相同。前提是对象上的equals 方法中所用的信息没有被修改。
如果 equals 比较的两个对象是相等的。那么hashCode 返回的 int 数必定是相等的
当equals 方法被重写时 ,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定。
1,若重写了 equals 方法,则有必要重写hashCode 方法
2,若 equals 比较的结果相等,那么 hashCOde 的结果一定相等。
3,若equals 比较的结果不相等,那么 hashCode 的结果 也有可能相等
4,若 hashCode 返回相同的 int 数,那么equals 不一定相等。
5,若 hashCode 返回不同的 int 数,那么 equals 一定不相等。
6,同一对象 在执行期将 若已经存储在集合中。则不能修改影响hashCode 值得相关信息。否则会导致 内存泄露的问题。
CharSequence
charSeqiemce 是一个接口,表示 char 值的一个可读序列。此接口对许多不同种类的 char 序列提供统一的 自读访问。此接口不修改 equals 和 hashCode 的常协规定,因此通常未定义比较实现 charSequence 的两个对象的结果。他有几个实现类:charBuffer 、String 、StringBuffer、StringBuilder。
CharSequence 和 String 都能用于定义字符串,但是 charSequence 的值是可读可写序列,而String 是 只读序列。
对一个抽象类或者是接口 ,不能使用new 来进行赋值。但是可以通过有一些的实例进行创建。
CharSequence cs = “hello”;
将资源文件转换为字符串
Latte.getApplication().getResources().getString(R.string.clear_history)
Latte.getApplication()为 Context
19/5-14
谈一谈startService 和 bindService 的区别,生命周期以及使用场景?
生命周期:
startService :onCreate -> onStartCommand 。然后服务会一直保持在运行状态,知道 stopService() 或者 stopSelf()方法被调用。注意:虽然每次 调用 startService() 方法,都会使 onStartCommand()方法执行一次。但是实际上只会存在一个实例。
bindService:onCreate() -> onBind(), 这个使用调用者和 Service 就会绑定在一起。第一次执行bindService 时,onCreate() 和 onBind()都会得到调用。但是多次调用时 onCreate()和onBind()方法并不会多次调用.
调用者如果获取绑定后的Service 的方法:
onBind回调方法将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。
既使用startService 又使用bindService 的情况:
如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStart方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。
那么,什么情况下既使用startService,又使用bindService呢?如果你只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果你还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService。前者的缺点是如果交流较为频繁,容易造成性能上的问题,而后者则没有这些问题。因此,这种情况就需要startService和bindService一起使用了。另外,如果你的服务只是公开一个远程接口,供连接上的客户端(Android的Service是C/S架构)远程调用执行方法,这个时候你可以不让服务一开始就运行,而只是bindService,这样在第一次bindService的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是远程服务,那么效果会越明显(当然在Servcie创建的是偶会花去一定时间,这点需要注意)。
Handler Thread 的使用场景和用法?
1,创建Handler Thread 实例对象
HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
1
2,启动线程
mHandlerThread.start();
1
3,创建Handler对象,重写handlerMessage方法
mHandler = new Handler(mHandlerThread.getLooper()){ @Override public void handleMessage(Message msg) { //这个方法是运行在mHandlerThread 线程 中的,可以执行耗时操作 } };
4,使用mHandler 向工作线程发送消息
mHandler.sendEmptyMessage(1); new Thread(new Runnable() { @Override public void run() { //在子线程给handler 发消息 mHandler.sendEmptyMessage(2); } });
5,结束线程,即停止线程的消息循环
mHandlerThread.quit();
Handler Thread的特点:
HandlerThread 将 loop 转到子线程中处理,说白了就是分担MainLooper 的工作量,降低了主线程的压力,使界面更流畅
开启一个线程起到多个线程的作用,如果任务是串行执行,按消息的发送顺序处理。HandlerThread 本质是一个线程,在线程内部,代码是串行处理的。
但是由于每一个任务都将以队列的方式举个被执行到,一旦列表中有某个任务执行时间过长。那么后续的会后被延时处理。
HandlerThrad 拥有自己的消息队列。他不会干扰或阻塞UI 线程
对于网络 IO 操作,HandlerThread 并不很合适,因为她只有一个线程,还得排队一个一个等着。
19/5-15
常用的颜色
<resources> <!--浅灰色--> <color name="app_background">#1111</color> <!--微信黑--> <color name="we_char_black">#323232</color> <!--item 的日常颜色--> <color name="item_background">#F1F1F1</color> <!--橘黄色--> <color name="app_main">@android:color/holo_orange_dark</color> </resources>
谈一谈ArrayMap 和 HashMap 的区别.
hashMap : 内部采用 数组和链表的结构。当链表中的长度大于8是,会转为红黑树。在查找的时候,因为是根据hashCode 来进行索引,所以查询速度会大幅度的提高。HashMap 的长度默认为 16 ,负载因子为 0.75 ,也就是说当HashMap 中的长度达到12的时候,就会自动扩容。扩容的大小为原来长度的 2 倍,但是如果 扩容。那么HashMap 中的所有元素 都会重新计算 HashCode值,并存放到扩容后的HashMap中,这是一个非常耗性能的操作。所以在使用HashMap 的时候,尽量在创建对象的时候给定 HashMap 的大小。HashMap 是不安全的。
ArrayMap:采用的是数组和数组的形式,一个保存哈希值,一个保存数据。在查找的时候 会根据二分查找法来查找元素。所以当数组中没扩容一次。就会导致查找的时候多一次判断。就是因为在这个原因,所以ArrayMap 不适合处理比较大的数据。在扩容的时候,如果长度大于8时申请大小 *1.5长度。大于4小于6时 申请 8 个长度。小于4时申请4个。这个比较其实是 ArrayMap 申请了更少的内存空间。但同时申请得频率就会变高。他不仅有扩容的功能,在删除时,如果集合中的元素少于一定的阈值。还会有收缩的功能,减少空间占用。所以在数据量小的时候,使用ArrayMap 会更好一些。ArrayMap 是不安全的。
ConvenientBanner 的使用(首页轮播图)
通用的广告栏控件,轻松实现广告头效果,可以设置自动翻页 和时间 ,非常智能,触碰暂停翻页,离开自动翻页。
简单的封装一下:
1,添加依赖,记得要加RecyclerView 的依赖。
api 'com.android.support:recyclerview-v7:28.0.0' api 'com.bigkoo:ConvenientBanner:2.1.4'
2,封装一个管理类:
public class BnnersCreator { public static void setDefault(ConvenientBanner<Integer> convenientBanner, ArrayList<Integer> banners, OnItemClickListener clickListener) { convenientBanner .setPages(new HolderCreate(),banners) //设置底部的小圆圈 .setPageIndicator(new int[]{R.drawable.dot_focus,R.drawable.dot_normal}) //设置小圆圈的位置 .setPageIndicatorAlign(ConvenientBanner.PageIndicatorAlign.CENTER_HORIZONTAL) //设置监听 .setOnItemClickListener(clickListener) //设置自动翻页的时间 .startTurning(3000) .setCanLoop(true); } }
3,创建 HolderCreate,并设置一个要显示item 的布局
public class HolderCreate implements CBViewHolderCreator { @Override public Holder createHolder(View itemView) { return new ImageHolder(itemView); } @Override public int getLayoutId() { return R.layout.index_photo; } }
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <android.support.v7.widget.AppCompatImageView android:id="@+id/index_photo" android:layout_width="match_parent" android:layout_height="200dp" android:background="@android:color/white" /> </android.support.v7.widget.LinearLayoutCompat>
4,创建Holder
public class ImageHolder extends Holder<Integer> { AppCompatImageView mImageview ; public ImageHolder(View itemView) { super(itemView); } @Override protected void initView(View itemView) { mImageview = itemView.findViewById(R.id.index_photo); } @Override public void updateUI(Integer data) { mImageview.setImageResource(data); } }
5,使用:
<com.bigkoo.convenientbanner.ConvenientBanner xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/banner_recycler_item" android:layout_width="match_parent" android:layout_height="200dp" android:orientation="vertical"/>
AppCompatImageView image = helper.getView(R.id.index_sort_1); image.setImageResource((Integer) item.getField(MultipleFields.IMAGE_URL)); protected void onPause() { super.onPause(); //停止轮播 cbTest1.stopTurning(); cbTest2.stopTurning(); }
上面只是加载本地的图片。如果要加载网络中的图片只需要将 泛型改为String 类型,使用Giilde 加载即可,如下所示:
public class ImageHolder extends Holder<String> { AppCompatImageView mImageview ; public ImageHolder(View itemView) { super(itemView); } @Override protected void initView(View itemView) { mImageview = itemView.findViewById(R.id.index_photo); } @Override public void updateUI(String data) { Glide.with(Coke.getAppContext()) .load(data) .diskCacheStrategy(DiskCacheStrategy.ALL) .dontAnimate() .centerCrop() .fitCenter() .into(mImageview); } }
Gilde 的使用
Glide是Google官方推荐的一个图片加载和缓存的开源库,它不仅能实现平滑的图片列表滚动效果,还支持远程图片的获取、大小调整和展示,并且可以加载GIF图片。Glide相比与UIF、Volley、Picasso、Fresco等其他框架的优点是轻量和稳定。
常用方法如下:
.with() 图片加载的环境:1,Context对象。2,Activity对象。3,FragmentActivity对象。4,Fragment对象
.load() 加载资源:1,drawable资源。2,本地File文件。3,uri。4,网络图片url。5,byte数组(可以直接加载GIF图片)
.placeholder() 图片占位符
.error() 图片加载失败时显示
.crossFade() 显示图片时执行淡入淡出的动画默认300ms
.dontAnimate() 不执行显示图片时的动画
.override() 设置图片的大小
.centerCrop() 和 fitCenter() 图片的显示方式
.animate() view动画 2个重构方法
.transform() bitmap转换
.bitmapTransform() bitmap转换。比如旋转,放大缩小,高斯模糊等(当用了转换后你就不能使用.centerCrop()或.fitCenter()了。)
.priority(Priority.HIGH) 当前线程的优先级
.signature(new StringSignature(“ssss”))
.thumbnail(0.1f) 缩略图,3个重构方法:优先显示原始图片的百分比(10%)
.listener() 异常监听
.into() 图片加载完成后进行的处理:1,ImageView对象。2,宽高值。3,Target对象
1,添加依赖和网络权限.
<uses-permission android:name="android.permission.INTERNET"/> api 'com.github.bumptech.glide:glide:4.9.0' api 'com.squareup.okhttp3:okhttp:3.12.0'
2,使用:
//基本用法 Glide.with(mContext) .load(imageUrl) /* * 图片的缓存: * DiskCacheStrategy.NONE 什么都不缓存 * DiskCacheStrategy.SOURCE 只缓存全尺寸图 * DiskCacheStrategy.RESULT 只缓存最终的加载图 * DiskCacheStrategy.ALL 缓存所有版本图(默认行为) */ .diskCacheStrategy(DiskCacheStrategy.ALL) // 在图片没有加载出来或加载失败时显示ic_launcher图片 .placeholder(R.mipmap.ic_launcher) .dontAnimate() //将他图片按比例缩放到足以填充imageView 的尺寸,但是图片可能显示不完整 .centerCrop() //将图片缩放到小于等于imageView的尺寸,这样图片会完整显示但是 imageView 就可能填不满了 .fitCenter() .into((ImageView) holder.getView(R.id.img_single)); //设置加载动画 Glide.with(this).load(imageUrl).animate(R.anim.item_alpha_in).into(imageView); //设置加载中 以及加载失败的图片 Glide .with(this) .load(imageUrl) .placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).into(imageView); //设置要加载的内容 项目中有很多需要先下载图片然后再做一些合成的功能,比如项目中出现的图文混排,该如何实现目标下 Glide.with(this).load(imageUrl).centerCrop().into(new SimpleTarget<GlideDrawable>() { @Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) { imageView.setImageDrawable(resource); } }); //多样式的媒体加载 Glide .with(context) .load(imageUrl); .thumbnail(0.1f);//设置缩略图支持:先加载缩略图 然后在加载全图 //传了一个 0.1f 作为参数,Glide 将会显示原始图像的10%的大小。 //如果原始图像有 1000x1000 像素,那么缩略图将会有 100x100 像素。 .asBitmap()//显示gif静态图片 .asGif();//显示gif动态图片 .into(imageView); //设置跳过内存缓存 Glide .with(this) .load(imageUrl) .skipMemoryCache(true) .into(imageView); //设置跳过内存缓存 //这意味着 Glide 将不会把这张图片放到内存缓存中去 //这里需要明白的是,这只是会影响内存缓存!Glide 将会仍然利用磁盘缓存来避免重复的网络请求 Glide.get(this).clearDiskCache();//清理磁盘缓存 需要在子线程中执行 Glide.get(this).clearMemory();//清理内存缓存 可以在UI主线程中进行
19/5-16
synchronized 和 volatitle 关键字的区别
synchronized 可以保证原子性。他可以保证 在同一时刻,只有一个线程可以访问被 synchronized 修饰的方法,或者代码块。
volatile 不能保证原子性。当时在使用这个关键字后。当被Volatitle 修饰字段的值发生改变后,其他线程会立刻知道这个值已经发生变化了。volatitle 可以保证可见性和有序性
1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的 volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
3.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
4.volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
加载 loading
AvLoadingIndicatorView 的封装
19/5-17
Default Activity not fount ,所有的项目都找不到Activity
今天碰到一个问题。不知道为什么项目打开时候,找不到Activity。程序没办法运行。所有的项目都是这个问题。
就在准备重装 AndroidStudio 的时候,找到了问题的所在。解决如下
C:\Users\Lv_345\ .AndroidStudio3.2\system\caches
首先关闭 as 。在用户目录下找到 .AndroidStudio3.2 ,然后找到 caches 文件夹,并且删除这个文件夹重启as即可。
造成这个的原因应该是异常关闭。
什么是冒泡排序,如何进行优化:
冒泡排序: 首先计算长度。
有两层循环。第一层循环控制比较的趟数,第二层则进行比较。
比较规则如下: 第一个元素和后面的元素挨个比较。完成之后,就是第二个元素和后面元素依次进行比较。知道倒着第一个和倒着的第二元素比较完后, 排序结束
int[] ints = {52,7,2,9,1,7,653,1,47,55,5}; for (int i = 0; i < ints.length; i++) { for (int j = i; j < ints.length; j++) { if (ints[i] > ints[j]){ int temp = ints[i]; ints[i] = ints[j]; ints[j] = temp; } } }
19/5-18
第三方数据库:greenrobot 的使用:
1,首先 添加依赖:
api 'org.greenrobot:greendao-generator:3.2.2' api 'org.greenrobot:greendao:3.2.2'
在项目的build.gradle 中添加:
//数据库 classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
在需要使用数据库的module 中添加:
/** * 数据库的插件 */ apply plugin: 'org.greenrobot.greendao'
2,进行一个小封装,并设置表名。
public class ReleaseOpenHelper extends DaoMaster.OpenHelper { public ReleaseOpenHelper(Context context, String name) { super(context, name); } }
public class DatabaseManager { private DaoSession mDaoSession = null; private DatabaseManager(){} public DatabaseManager init(Context context){ initDao(context); return this; } /** * 内部类 的单例模式 */ private static final class Holder{ private static final DatabaseManager INSTANCE = new DatabaseManager(); } /** * @return 返回实例 */ public static DatabaseManager getInstance(){ return Holder.INSTANCE; } /** * 初始化,并创建表 */ private void initDao(Context context){ //自定义的类:ReleaseOpenHelper 继承自 DaoMaster.OpenHelper //通过 DaoMaster的内部类 可以得到 SQLiteOpenHelper 的对象 // 第二个参数为 数据库的名称 final ReleaseOpenHelper helper = new ReleaseOpenHelper(context,"task.db"); //获取可读写 数据库 final Database db =helper.getWritableDb(); //拿到 DaoSession 对象 mDaoSession = new DaoMaster(db).newSession(); } public final DaoSession getDao(){ return mDaoSession; } }
3,初始化
DatabaseManager.getInstance().init(this);
4,创建表
@Entity public class UserProfile { @Id private String number; private String password; ...... }
@Entity 注解表示这是一个表。@Id 表示这是主键key ,创建完字段后 Rebuild 一下就会生成相应的代码。
5,插入数据:
UserProfile userProfile = new UserProfile("张三","123456"); DaoSession dao = DatabaseManager.getInstance().getDao(); dao.getUserProfileDao().insertOrReplace(userProfile);
6,修改数据:
//修改数据 DaoSession dao = DatabaseManager.getInstance().getDao(); //根据对应的主句 获取对应列的对象 UserProfile load = dao.getUserProfileDao().load("主键"); //修改数据 load.setPassword("我是被修改的密码"); //更新数据 dao.update(load);