简要说说LruCache 的原理
LruCache 非常适合用于 缓存图片,他的主要算法原理是包最近使用的对象 存储在 LinckHashMap中,并且把最近使用的最少的对象在 缓存值达到预设值之前从内存中移除
简单的封装如下
public class LruCachePhoto { /** * 图片 缓存技术的核心类,用于缓存下载好的所有图片, * 在程序内存达到设定值后会将最少最近使用的图片移除掉 */ private LruCache<String, Bitmap> mMenoryCache; public LruCachePhoto() { //获取应用最大可用内存 int maxMemory = (int) Runtime.getRuntime().maxMemory(); //设置 缓存文件大小为 程序最大可用内存的 1/8 int cacheSize = maxMemory / 8; mMenoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getByteCount(); } }; } /** * 从 LruCache 中获取一张图片,如果不存在 就返回 null * * @param key LurCache 的键,这里是 图片的地址 * @return 返回对应的 Bitmap对象,找不到则为 null */ public Bitmap getBitmapFromMemoryCache(String key) { return mMenoryCache.get(key); } /** * 添加一张图片 * * @param key key * @param bitmap bitmap */ public void addBitmapToCache(String key, Bitmap bitmap) { if (getBitmapFromMemoryCache(key) == null) { mMenoryCache.put(key, bitmap); } } }
19/6-21
Failed to transform artifact ‘butterknife-runtime.aar (com.jakewharton:butterknife-runtime:10.1.0)’
报错信息如下:
Execution failed for task ‘:app:mergeExtDexDebug’.
Could not resolve all files for configuration ‘:app:debugRuntimeClasspath’.
Failed to transform artifact ‘butterknife-runtime.aar (com.jakewharton:butterknife-runtime:10.1.0)’ to match attributes {artifactType=android-dex, dexing-is-debuggable=true, dexing-min-sdk=15}
Execution failed for DexingTransform: C:\Users\Lv_345.gradle\caches\transforms-2\files-2.1\8e0adaeeb74965e53b877012710dd195\jars\classes.jar.
Error while dexing.
解决:给所有的 Module 加入jdk1.8
在 build.gradle 的 android 下加入下面代码 即可解决
compileOptions { sourceCompatibility = '1.8' targetCompatibility = '1.8' }
19/6-24
PHP 连接数据库
$like = mysqli_connect("192.168.167.2","345","111111","frame"); if (!$like){ echo "连接数据库失败"; }
PHP 获取 get 请求数据
//获取get 请求的数据 if ($_GET) { if (isset($_GET['name'], $_GET['age'])) { echo $_GET['name'] . "----" . $_GET['age']; } }
PHP 获取 post 请求的 json 数据、
//获取 post 请求的json数据 if (empty($_POST)) { //获取json数据 $data = file_get_contents('php://input'); //将json 解析为数组 $Content = json_decode($data,true); $abc =$Content['name']; echo $abc; }
PHP 返回一个 json 数据
$result["result"] = "ok"; $result["data"] = $array; //将数组 转为 json 串 $result = json_encode($result, JSON_UNESCAPED_UNICODE); echo $result;
PHP 执行 sql 查询 语句
$like->set_charset("utf8"); $sql = "SELECT * FROM `account` "; $mysqli_result = $like->query($sql); if (!$mysqli_result) { $res['code'] = 200; $res['result'] = "error"; echo json_encode($res,JSON_UNESCAPED_UNICODE); }else{ while ($temp = $mysqli_result->fetch_assoc()){ if ($email == $temp['email']){ if ($password == $temp['password']){ $res['code'] = 200; $res['result'] = "success"; echo json_encode($res,JSON_UNESCAPED_UNICODE); } } } }
19/6-25 ——26
Callable 与 Runnable
先看一下Runnable ,他是一个接口,提供了一个 run方法
public interface Runnable { public abstract void run(); }
由于他的返回值是 run,所以在执行完任务后没有任何返回结果
下面看一下 Callable ,他也是一个接口,有一个方法,名字为 call() ;
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
这是一个泛型接口,并且带有返回值,返回值也是一个 泛型。
Future
Future 就是对具体的Runnable 或者 Callable 任务执行结果进行取消,查询是否完成·,后去结果,必要时可以通过get 方法获取执行结果,该方法会左侧知道任务返回结果
Future 是一个接口
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
cancel 方法用来 取消任务,成功返回 true,失败返回 false。 参数表示允许取消正在执行却没有执行完毕的任务,如果设置为 true,则表示可以取消在执行过程中的任务,如任务已完成,则无论参数为 true 或者 false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若参数为 true ,则返回 true,若 参数为 false,如任务没有执行,则不论参数为 true 或者false,肯定返回true;
isCancelled 表示任务是否被成功取消,如果任务在正常完成前被取消成功,则返回true。
isDone 方法表示任务是否已经完成,若任务已经完成,返回true。
get 获取任务执行的结果,该方法会产生阻塞,指定任务执行完毕 才返回。
get(long timeout ,TimeUnit unit) 获取执行结果,还没获取到结果就直接返回 null
也就是说 Future 提供了三种功能:
1,判断任务是否完成
2,中断任务
3,能够获取任务的执行结果
因为 Future 是一个接口,没办法用来创建对象使用,所有就有了下面的 FutureTask
FutureTask
FutureTask 的实现
public class FutureTask<V> implements RunnableFuture<V> { }
public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); }
可以看出 FutureTask 实现了 Runnable 和 Future 接口,所以他既可以作为Runnable 被线程执行,又可以作为Future 得到 Callable 的返回值
FutureTask 提供了两个构造器
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
FutureTask 是Future 接口的唯一实现类
使用示例
Callable+Future 获取执行结果
//使用 Callable + Future 获取执行结果 Callable<Integer> callable = () -> { int sum = 0; for (int i = 0; i < 100; i++) { sum += i; } return sum; }; ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> result = executor.submit(callable); executor.shutdown();//启动任务 try { System.out.println("线程返回结果"+result.get()); } catch (Exception e) { e.printStackTrace(); }
Callable + FutureTask 获取执行结果
//使用 Callable + FutureTask 获取执行结果 Callable<Integer> callable = () -> { int sum = 0; for (int i = 0; i < 100; i++) { sum += i; } return sum; }; //第一种方式 FutureTask<Integer> futureTask = new FutureTask<>(callable); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(futureTask); executor.shutdown();//执行任务 /* //第二种方式 ,和第一种差不多,只不过一个是线程池,一个是 Thread FutureTask<Integer> future = new FutureTask<>(callable); Thread thread = new Thread(future); thread.start();*/ try { System.out.println(futureTask.get()); } catch (Exception e) { e.printStackTrace(); }
使用 FutureTask 的好处
FutureTask 是为了 弥补 Thread 的不足而设计的,他可以让程序员准确的知道线程什么时候执行完,并获得线程执行案后返回的结果,Future 可以取消异步任务,他的计算是通过 Callable 来实现的。他等价可以携带的结果的Tunnable ,并且有三个状态,等待 ,运行,和完成。完成包括所以计算已任意方式结束,正常结束,取消和 异常。
android 中更多的线程
除了上面这些,在android中充当线程的角色 还有 AsyncTask,HandlerThread,IntentService。他们本质上都是由HandlerThread 来构成的,
AsyncTask
他封装了 线程池 和 Handler ,主要为我们在子线程中更新 UI 提供便利
它是一个抽象的泛型类,声明:
public abstract class AsyncTask {......
参数1,Params,异步任务的入参;
参数2,Progress,执行任务的进度;
参数3,Result,后台任务执行的结果;
方法1, onPreExecute(),在主线程中执行,任务开启前的准备工作;
方法2,doInbackground(Params…params),开启子线程执行后台任务;
方法3,onProgressUpdate(Progress values),在主线程中执行,更新UI进度;
方法4,onPostExecute(Result result),在主线程中执行,异步任务执行完成后执行,它的参数是doInbackground()的返回值。
从上面可以清楚的看到AsynTask的具体用法。
AsyncTask源码分析
HandlerThread HandlerThread 继承了 Thread,也实现了run方法,我们先看一下使用方式: HandlerThread thread = new HandlerThread("HandlerThread"); thread.start(); mHandler = new Handler(thread.getLooper()){ @Override public void handleMessage(Message msg) { Log.e("------", "handleMessage: "+msg.obj ); } }; new Thread(() -> { String str = "子线程发送数据"; Message obtain = Message.obtain(); obtain.obj = str; mHandler.sendMessage(obtain); }).start(); String str = "主线程发送数据"; Message obtain = Message.obtain(); obtain.obj = str; mHandler.sendMessage(obtain); //当名确不在使用 HandlerThread,则需要终止线程的运行 thread.quit();
首先创建 HandlerThread 的实例,接着创建Handler,最后分别在主线程和 子线程发送消息。
下面看一下分析一下 ThreadThread。
首先看 他的run 方法
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
在run方法中(run方法里面的逻辑是在子线程执行的),创建了一个子线程的looper,然后将 looper 给 mLooper。最后调用loop 方法,不断的从 MessageQueue 中取出消息,没有消息就阻塞,有消息就会唤醒。
我们在创建 Handler 的时候是这样创建的,这样 我们的mHandler 和 Looper 就在一个线程中了。
mHandler = new Handler(thread.getLooper()){......}
getLooper 的源码:
public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
他的返回值就是 在run 方法中创建的 mLooper,所以 getLooper 必须在 start 之后调用,
当 mHandler 发送消息后,会把这个消息添加进 队列。然后Looper.loop就会取出消息,交给Handler 进行分发。到这里分析完了。
但是还有一点需要说一下,在 run 方法中 mLooper 创建完后 有notifyAll ,getLooper() 中有 wait ,这是为什么呢? 因为在创建 Handler 对象时 需要用到 mLooper ,但是如果 run方法 还么有创建 mLooper ,就会获取不到 mLooper。所以在这里进行 wait(),也就是说 我们必须等到 mLooper 的创建完成。
总结一下:
HandlerThread 就相当于在子线程创建了 一个 消息循环。说白了就是分担 主线程的工作量,降低了主线程的压力,HandlerThread 处理 任务是 串行执行,按照发送的顺序进行处理,HandlerThread 本质是一个县城,在线程内部,代码是串行执行的,如果一个 任务执行的实现过程,那么就导致后续的任务都被延迟处理。HandlerThread 有自己的队列,不会打扰 到 主线程。
IntentService
IntentService 是一个 继承Service 的抽象类,因此必须创建他的子类 才可以使用IntentService ,IntentService 用来执行后台耗时的任务,当任务执行后他会自动停止,同时由于IntentService 是服务的原因,他比较适合执行以下优先级高的任务,IntentService封装了 Handler 和 Thread ,和 HandlerThread 非常相似。
下面分析一下源码:
private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); } } @Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); }
当 IntentService 被第一次启动时,onCreate 会被调用,onCreate 会创创建一个 HandlerService,然后 通过他的 Looper 创建一个 ServiceHandler 对象。这样只要使用的是 mServiceHandler 发送的消息 都会在 HandlerThread 中执行,最后在分发到 ServiceHandler 的 handleMessage。
我们重复启动某个服务时,不会开启新的服务,只是会调用 onStartCommand 方法。onStartCommand 就会去调用 onStart() 方法。如下所示:
public int onStartCommand(@Nullable Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onStart(@Nullable Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); }
在onStart 中,使用mServiceHandler 发送了一个消息,这个消息在 HandlerThread 里面被处理,然后被 mServcieHandler 接收,接着就会调用 onHandleInteger 处理消息。
处理完消息后就会 调用 stopSelf(msg.arg1) 来尝试停止任务,这里之所以采用 stopSefl( int startId ),而不是使用 stopSelf 是因为 stopSelf 会立刻停止服务,但是这个时候可能有其他 消息没有处理。stopSelf(int startId ) 则会等待所有的消息处理完毕后才终止 服务,
当我们有多个后台任务时,就需要执行多次 启动 IntentService 。Handler 中的 Looper 是顺序处理消息的,所以IntentService 也是 顺序处理消息的,有多个任务需要执行时,这些任务会按照 发起的顺序排序执行。
使用如下:
public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(@Nullable Intent intent) { String action = intent.getStringExtra("service"); Log.e("---------", "onHandleIntent: "+action ); } @Override public void onDestroy() { super.onDestroy(); Log.e("---------", "onDestroy: 服务销毁" ); } }
Intent intent = new Intent(this,MyIntentService.class); intent.putExtra("service","消息1"); startService(intent); Intent intent1 = new Intent(this,MyIntentService.class); intent1.putExtra("service","消息2"); startService(intent1); Intent intent2 = new Intent(this,MyIntentService.class); intent2.putExtra("service", "消息3"); startService(intent2);
19/6-27
谈谈ThreadLocal 的用法和 原理:
ThreadLocal 用来保存数据,且每个线程之间互不影响,比如 在主线程保存了一个 对象,但是在子线程中 就无法找到这个对象。
ThreadLocal 是一个线程内部的数据存储类,通过他可以指定线程中存储的数据,在读取时,只有指定的线程才可以读取到数据,其他线程无法拿到数据。
ThreadLocal 在保存数据时 会获取当前操作的线程,然后将数据保存在当前线程的 ThreadLocalMap 中,以当前的对象为键,数据为值,进行保存。
ThreadLoca 在获取数据时,和保存非常相似,都是拿到当前的线程对象,然后去判断 线程对象的 ThreadLocalMap 是否为空,如果为空,则 没有数据,否则 则获取数据。
从ThreadLocal的set 和 get 方法可以看出,他们操作的都是根据当前线程中的ThreadLocal.ThreadLocalMap threadLocals = null 来判断当前线程有没有保存数据,如果保存了,就会在当前线程中产生一个ThreadLocalMap 的对象 。数据就会保存在这个对象 里面,如果 没有保存过数据,那么当前线程中的ThreadLocalMap 就会为空。他们对ThreadLocal 所做的读/写操作仅限于线程的内部。因此在不同线程中访问同一个ThreadLocal的 set 和 get 方法 所得到的值 也是不一样的。
JVM ,Dalvik ,ART , 三者的原理和区别
JVM : 是java 虚拟机的缩写,其并不是指某个特定的虚拟机实现,而是指能够运行 java 字节码(类文件) 的虚拟机实现,
Dalvik : 是谷歌写的一个用于 android 的虚拟机,但严格来说并不算 JVM(没有遵循 java 虚拟机的规范,比如字节码格式是 DEX ,而非 .class)
ART : 是 Android Runntime 的缩写,严格来时并不是一个虚拟机,ART在安卓4.4时加入,5.0取代的Dalvik作为唯一实现直到现在。
java 中的 wait ,notify,notifyAll
wait:线程自动释放其占有的锁,导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。
notify :唤醒正在等待对象监视器的单个线程。
notifyAll :唤醒正在等待对象监视器的所有线程。
CountDownLatch
CountDownLatch 的作用是 倒数,每当一个线程执行完任务后,他就会减一,直到为 0 时 就代表所有的线程的任务都执行完了。总得来说 CountDownLatch 就是等待其他线程执行完任务。
详见 android/Thread/CountDownLatch 控制多线程并发等待
android 中访问资源的方式
引用自定义资源。格式 @[package:]type/name
android:text="@string/hello wold"
引用系统资源,格式 @android:type/name
android:textColor:"@android:color/opaque_red"
@* 代表引用系统的非public 资源,格式 @*android:type/name
系统资源定义分 public 和 非public,public 的声明在
\platforms\android-8\data\res\values\public.xml
**@*android:type/name:**可以调用系统定义的所有资源
**@android:type/name:**只能够调用public属性的资源。
注意:没有在 public.xml 中声明的资源是 google不推荐使用的
?代表引用主题属性
另外一种资源值 允许你引用当前主题中的属性的值。这属性值能能在style资源 和 XML 属性中使用,它允许你通过他们改变为 当前主题提供标准变化来改变 UI 元素的外观。例如:
android:textColor="?android:textDisabledColor"
@+ 表示在创建或者引用资源,格式 @+ type/name
@+id/资源ID名 新建一个资源ID
@id/资源ID名 应用现有已定义的资源ID,包括系统ID
@android:id/资源ID名 引用系统ID,其等效于@id/资源ID名
android:id="@+id/selectdlg"
android:id="@android:id/text1"
android:id="@id/button3"
参考自:https://blog.csdn.net/mingli198611/article/details/7105850
android 根据资源名字获取资源 id
首先看一个布局
<Button android:id="@+id/index_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" />
然后通过一个字符串 找到这个id
int index_btn = res.getIdentifier("index_btn", "id", getProxyActivity().getPackageName()); Button button = rootView.findViewById(index_btn); button.setOnClickListener((view)->{ Toast.makeText(getContext(), "哈哈哈", Toast.LENGTH_SHORT).show(); });
第一个参数为 名字,第二个就是 类型,第三个 则是包名
android:textColor:"@android:color/opaque_red"
@* 代表引用系统的非public 资源,格式 @*android:type/name
系统资源定义分 public 和 非public,public 的声明在
\platforms\android-8\data\res\values\public.xml
**@*android:type/name:**可以调用系统定义的所有资源
**@android:type/name:**只能够调用public属性的资源。
注意:没有在 public.xml 中声明的资源是 google不推荐使用的
?代表引用主题属性
另外一种资源值 允许你引用当前主题中的属性的值。这属性值能能在style资源 和 XML 属性中使用,它允许你通过他们改变为 当前主题提供标准变化来改变 UI 元素的外观。例如:
android:textColor="?android:textDisabledColor"
@+ 表示在创建或者引用资源,格式 @+ type/name
@+id/资源ID名 新建一个资源ID
@id/资源ID名 应用现有已定义的资源ID,包括系统ID
@android:id/资源ID名 引用系统ID,其等效于@id/资源ID名
android:id="@+id/selectdlg"
android:id="@android:id/text1"
android:id="@id/button3"
参考自:https://blog.csdn.net/mingli198611/article/details/7105850
android 根据资源名字获取资源 id
首先看一个布局
<Button android:id="@+id/index_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" />
然后通过一个字符串 找到这个id
int index_btn = res.getIdentifier("index_btn", "id", getProxyActivity().getPackageName()); Button button = rootView.findViewById(index_btn); button.setOnClickListener((view)->{ Toast.makeText(getContext(), "哈哈哈", Toast.LENGTH_SHORT).show(); });
第一个参数为 名字,第二个就是 类型,第三个 则是包名