+ (void)setDefaultStyle:(SVProgressHUDStyle)style; // default is SVProgressHUDStyleLight + (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType; // default is SVProgressHUDMaskTypeNone + (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type; // default is SVProgressHUDAnimationTypeFlat + (void)setContainerView:(UIView*)containerView; // default is window level + (void)setMinimumSize:(CGSize)minimumSize; // default is CGSizeZero, can be used to avoid resizing + (void)setRingThickness:(CGFloat)width; // default is 2 pt + (void)setRingRadius:(CGFloat)radius; // default is 18 pt + (void)setRingNoTextRadius:(CGFloat)radius; // default is 24 pt + (void)setCornerRadius:(CGFloat)cornerRadius; // default is 14 pt + (void)setBorderColor:(nonnull UIColor*)color; // default is nil + (void)setBorderWidth:(CGFloat)width; // default is 0 + (void)setFont:(UIFont*)font; // default is [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline] + (void)setForegroundColor:(UIColor*)color; // default is [UIColor blackColor], only used for SVProgressHUDStyleCustom + (void)setBackgroundColor:(UIColor*)color; // default is [UIColor whiteColor], only used for SVProgressHUDStyleCustom + (void)setBackgroundLayerColor:(UIColor*)color; // default is [UIColor colorWithWhite:0 alpha:0.4], only used for SVProgressHUDMaskTypeCustom + (void)setImageViewSize:(CGSize)size; // default is 28x28 pt + (void)setInfoImage:(UIImage*)image; // default is the bundled info image provided by Freepik + (void)setSuccessImage:(UIImage*)image; // default is bundled success image from Freepik + (void)setErrorImage:(UIImage*)image; // default is bundled error image from Freepik + (void)setViewForExtension:(UIView*)view; // default is nil, only used if #define SV_APP_EXTENSIONS is set + (void)setGraceTimeInterval:(NSTimeInterval)interval; // default is 0 seconds + (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval; // default is 5.0 seconds + (void)setMaximumDismissTimeInterval:(NSTimeInterval)interval; // default is CGFLOAT_MAX + (void)setFadeInAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds + (void)setFadeOutAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds + (void)setMaxSupportedWindowLevel:(UIWindowLevel)windowLevel; // default is UIWindowLevelNormal + (void)setHapticsEnabled:(BOOL)hapticsEnabled; // default is NO
/** A node in linked map. Typically, you should not use this class directly. */ @interface _YYLinkedMapNode : NSObject { @package __unsafe_unretained _YYLinkedMapNode *_prev; // retained by dic __unsafe_unretained _YYLinkedMapNode *_next; // retained by dic id _key; id _value; NSUInteger _cost; NSTimeInterval _time; } @end @implementation _YYLinkedMapNode @end /** A linked map used by YYMemoryCache. It's not thread-safe and does not validate the parameters. Typically, you should not use this class directly. */ @interface _YYLinkedMap : NSObject { @package CFMutableDictionaryRef _dic; // do not set object directly NSUInteger _totalCost; NSUInteger _totalCount; _YYLinkedMapNode *_head; // MRU, do not change it directly _YYLinkedMapNode *_tail; // LRU, do not change it directly BOOL _releaseOnMainThread; BOOL _releaseAsynchronously; }
/// Insert a node at head and update the total cost. /// Node and node.key should not be nil. - (void)insertNodeAtHead:(_YYLinkedMapNode *)node;
/// Bring a inner node to header. /// Node should already inside the dic. - (void)bringNodeToHead:(_YYLinkedMapNode *)node;
/// Remove a inner node and update the total cost. /// Node should already inside the dic. - (void)removeNode:(_YYLinkedMapNode *)node;
/// Remove tail node if exist. - (_YYLinkedMapNode *)removeTailNode;
/// Remove all node in background queue. - (void)removeAll;
- (void)flushMessageQueue:(NSString *)messageQueueString{ if (messageQueueString == nil || messageQueueString.length == 0) { NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page."); return; }
id messages = [self _deserializeMessageJSON:messageQueueString]; for (WVJBMessage* message in messages) { if (![message isKindOfClass:[WVJBMessage class]]) { NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message); continue; } [self _log:@"RCVD" json:message];
function _handleMessageFromObjC(messageJSON) { _dispatchMessageFromObjC(messageJSON); }
function _dispatchMessageFromObjC(messageJSON) { if (dispatchMessagesWithTimeoutSafety) { setTimeout(_doDispatchMessageFromObjC); } else { _doDispatchMessageFromObjC(); }
function _doDispatchMessageFromObjC() { var message = JSON.parse(messageJSON); var messageHandler; var responseCallback;
if (message.responseId) { responseCallback = responseCallbacks[message.responseId]; if (!responseCallback) { return; } responseCallback(message.responseData); delete responseCallbacks[message.responseId]; } else { if (message.callbackId) { var callbackResponseId = message.callbackId; responseCallback = function(responseData) { _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData }); }; }
var handler = messageHandlers[message.handlerName]; if (!handler) { console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message); } else { handler(message.data, responseCallback); } } } }
object SsbKtx {
const val flag = SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE
const val type_bold = Typeface.BOLD
const val type_italic = Typeface.ITALIC
const val type_bold_italic = Typeface.BOLD_ITALIC
/**
*设置文字颜色
* @param range
* @return
*/
fun CharSequence.foregroundColorSpan(range: IntRange, color: Int = Color.RED): CharSequence {
return SpannableStringBuilder(this).apply {
setSpan(ForegroundColorSpan(color), range.first, range.last, SsbKtx.flag)
}
}
/**
*设置click,将一段文字中指定range的文字添加颜色和点击事件
* @param range
* @return
*/
fun CharSequence.clickSpan(
range: IntRange,
color: Int = Color.RED,
isUnderlineText: Boolean = false,
clickAction: () -> Unit
): CharSequence {
return SpannableString(this).apply {
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
clickAction()
}
override fun updateDrawState(ds: TextPaint) {
ds.color = color
ds.isUnderlineText = isUnderlineText
}
}
setSpan(clickableSpan, range.first, range.last, SsbKtx.flag)
}
}
//-------------------TextView相关扩展--------------------------
/**
*设置目标文字大小, src,target 为空时,默认设置整个 text
* @return
*/
fun TextView?.sizeSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
@DimenRes textSize: Int
): TextView? {
return when {
this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
textSize == 0 -> this
range != null -> {
text = src.sizeSpan(range, ResUtils.getDimensionPixelSize(textSize))
this
}
target.isNotNullOrEmpty() -> {
text = src.sizeSpan(src.range(target!!), ResUtils.getDimensionPixelSize(textSize))
this
}
else -> this
}
}
/**
*设置目标文字大小, src,target 为空时,默认设置整个 text
* @return
*/
fun TextView?.sizeSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
textSize: Float
): TextView? {
return when {
this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
textSize == 0f -> this
range != null -> {
text = src.sizeSpan(range, DensityUtils.dp2px(textSize))
this
}
target.isNotNullOrEmpty() -> {
text = src.sizeSpan(src.range(target!!), DensityUtils.dp2px(textSize))
this
}
else -> this
}
}
fun TextView?.appendSizeSpan(str: String?, @DimenRes textSize: Int): TextView? {
str?.let {
this?.append(it.sizeSpan(0..it.length, ResUtils.getDimensionPixelSize(textSize)))
}
return this
}
/**
*设置目标文字类型(加粗,倾斜,加粗倾斜),src,target 为空时,默认设置整个 text
* @return
*/
fun TextView?.typeSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
type: Int
): TextView? {
return when {
this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.typeSpan(range, type)
this
}
target.isNotNullOrEmpty() -> {
text = src.typeSpan(src.range(target!!), type)
this
}
else -> this
}
}
fun TextView?.appendTypeSpan(str: String?, type: Int): TextView? {
str?.let {
this?.append(it.typeSpan(0..it.length, type))
}
return this
}
/**
*设置目标文字下划线
* @return
*/
fun TextView?.underlineSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null
): TextView? {
return when {
this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.underlineSpan(range)
this
}
target.isNotNullOrEmpty() -> {
text = src.underlineSpan(src.range(target!!))
this
}
else -> this
}
}
/**
*设置目标文字对齐方式
* @return
*/
fun TextView?.alignSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
align: Layout.Alignment
): TextView? {
return when {
this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.alignSpan(range, align)
this
}
target.isNotNullOrEmpty() -> {
text = src.alignSpan(src.range(target!!), align)
this
}
else -> this
}
}
fun TextView?.appendAlignSpan(str: String?, align: Layout.Alignment): TextView? {
str?.let {
this?.append(it.alignSpan(0..it.length, align))
}
return this
}
/**
*设置目标文字超链接
* @return
*/
fun TextView?.urlSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
url: String
): TextView? {
return when {
this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
movementMethod = LinkMovementMethod.getInstance()
text = src.urlSpan(range, url)
this
}
target.isNotNullOrEmpty() -> {
movementMethod = LinkMovementMethod.getInstance()
text = src.urlSpan(src.range(target!!), url)
this
}
else -> this
}
}
fun TextView?.appendUrlSpan(str: String?, url: String): TextView? {
str?.let {
this?.append(it.urlSpan(0..it.length, url))
}
return this
}
/**
*设置目标文字点击
* @return
*/
fun TextView?.clickIntSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
color: Int = Color.RED,
isUnderlineText: Boolean = false,
clickAction: () -> Unit
): TextView? {
return when {
this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT // remove click bg color
text = src.clickSpan(range, color, isUnderlineText, clickAction)
this
}
target.isNotNullOrEmpty() -> {
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT // remove click bg color
text = src.clickSpan(src.range(target!!), color, isUnderlineText, clickAction)
this
}
else -> this
}
}
fun TextView?.appendClickIntSpan(
str: String?, color: Int = Color.RED,
isUnderlineText: Boolean = false,
clickAction: () -> Unit
): TextView? {
str?.let {
this?.append(it.clickSpan(0..it.length, color, isUnderlineText, clickAction))
}
return this
}
/**
*设置目标文字点击
* @return
*/
fun TextView?.clickSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
@ColorRes color: Int,
isUnderlineText: Boolean = false,
clickAction: () -> Unit
): TextView? {
return when {
this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT // remove click bg color
text = src.clickSpan(range, ResUtils.getColor(color), isUnderlineText, clickAction)
this
}
target.isNotNullOrEmpty() -> {
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT // remove click bg color
text = src.clickSpan(
src.range(target!!),
ResUtils.getColor(color),
isUnderlineText,
clickAction
)
this
}
else -> this
}
}
/**扫描jar*/
fun scanJar(jarFile: File, dest: File?) {
val file = JarFile(jarFile)
var enumeration = file.entries()
while (enumeration.hasMoreElements()) {
val jarEntry = enumeration.nextElement()
if (jarEntry.name.endsWith("XXRouterTable.class")) {
val inputStream = file.getInputStream(jarEntry)
val classReader = ClassReader(inputStream)
if (Arrays.toString(classReader.interfaces)
.contains("IHTRouterTBCollect")
) {
tableList.add(
Pair(
classReader.className,
dest?.absolutePath
)
)
}
inputStream.close()
} else if (jarEntry.name.endsWith("HTRouterInitializer.class")) {
registerInitClass = dest
}
}
file.close()
}
对目标Class注入路由表初始化代码
fun asmInsertMethod(originFile: File?) {
val optJar = File(originFile?.parent, originFile?.name + ".opt")
if (optJar.exists())
optJar.delete()
val jarFile = JarFile(originFile)
val enumeration = jarFile.entries()
val jarOutputStream = JarOutputStream(FileOutputStream(optJar))
while (enumeration.hasMoreElements()) {
val jarEntry = enumeration.nextElement()
val entryName = jarEntry.getName()
val zipEntry = ZipEntry(entryName)
val inputStream = jarFile.getInputStream(jarEntry)
//插桩class
if (entryName.endsWith("RouterInitializer.class")) {
//class文件处理
jarOutputStream.putNextEntry(zipEntry)
val classReader = ClassReader(IOUtils.toByteArray(inputStream))
val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
val cv = RegisterClassVisitor(Opcodes.ASM5, classWriter,tableList)
classReader.accept(cv, EXPAND_FRAMES)
val code = classWriter.toByteArray()
jarOutputStream.write(code)
} else {
jarOutputStream.putNextEntry(zipEntry)
jarOutputStream.write(IOUtils.toByteArray(inputStream))
}
jarOutputStream.closeEntry()
}
//结束
jarOutputStream.close()
jarFile.close()
if (originFile?.exists() == true) {
Files.delete(originFile.toPath())
}
optJar.renameTo(originFile)
}
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{ // First, check if the class has already been loaded
Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { // 先从父类加载器中进行加载
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader
}
if (c == null) { // 没有找到,再自己加载
c = findClass(name);
}
} return c;
}
复制代码
android.content.ActivityNotFoundException: Unable to find explicit activity class{com.zyg.commontec/com.zyg.plugin.PluginActivity}; have you declared this activity in your AndroidManifest.xml?
publicclassInstrumentation{ publicstaticvoidcheckStartActivityResult(int res, Object intent){ if (!ActivityManager.isStartResultFatalError(res)) { return;
}
switch (res) { case ActivityManager.START_INTENT_NOT_RESOLVED: case ActivityManager.START_CLASS_NOT_FOUND: if (intent instanceof Intent && ((Intent)intent).getComponent() != null) thrownew ActivityNotFoundException( "Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?"); thrownew ActivityNotFoundException( "No Activity found to handle " + intent);
...
}
}
}
public class WakeReceiver extends BroadcastReceiver {
private final static String TAG = WakeReceiver.class.getSimpleName();
private final static int WAKE_SERVICE_ID = -1111;
/**
* 灰色保活手段唤醒广播的action
*/
public final static String GRAY_WAKE_ACTION = "com.wake.gray";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (GRAY_WAKE_ACTION.equals(action)) {
Log.i(TAG, "wake !! wake !! ");
Intent wakeIntent = new Intent(context, WakeNotifyService.class);
context.startService(wakeIntent);
}
}
/**
* 用于其他进程来唤醒UI进程用的Service
*/
public static class WakeNotifyService extends Service {
@Override
public void onCreate() {
Log.i(TAG, "WakeNotifyService->onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "WakeNotifyService->onStartCommand");
if (Build.VERSION.SDK_INT < 18) {
startForeground(WAKE_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隐藏Notification上的图标
} else {
Intent innerIntent = new Intent(this, WakeGrayInnerService.class);
startService(innerIntent);
startForeground(WAKE_SERVICE_ID, new Notification());
}
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onDestroy() {
Log.i(TAG, "WakeNotifyService->onDestroy");
super.onDestroy();
}
}
/**
* 给 API >= 18 的平台上用的灰色保活手段
*/
public static class WakeGrayInnerService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "InnerService -> onStartCommand");
startForeground(WAKE_SERVICE_ID, new Notification());
//stopForeground(true);
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* 普通的后台Service进程
*
* @author clock
* @since 2016-04-12
*/
public class BackgroundService extends Service {
private final static String TAG = BackgroundService.class.getSimpleName();
@Override
public void onCreate() {
Log.i(TAG, "onCreate");
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
}
}
/**
* 灰色保活手法创建的Service进程
*
* @author Clock
* @since 2016-04-12
*/
public class GrayService extends Service {
private final static String TAG = GrayService.class.getSimpleName();
/**
* 定时唤醒的时间间隔,5分钟
*/
private final static int ALARM_INTERVAL = 5 * 60 * 1000;
private final static int WAKE_REQUEST_CODE = 6666;
private final static int GRAY_SERVICE_ID = -1001;
@Override
public void onCreate() {
Log.i(TAG, "GrayService->onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "GrayService->onStartCommand");
if (Build.VERSION.SDK_INT < 18) {
startForeground(GRAY_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隐藏Notification上的图标
} else {
Intent innerIntent = new Intent(this, GrayInnerService.class);
startService(innerIntent);
startForeground(GRAY_SERVICE_ID, new Notification());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "InnerService -> onStartCommand");
startForeground(GRAY_SERVICE_ID, new Notification());
//stopForeground(true);
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
private final static String TAG = WhiteService.class.getSimpleName();
private final static int FOREGROUND_ID = 1000;
@Override
public void onCreate() {
Log.i(TAG, "WhiteService->onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "WhiteService->onStartCommand");
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle("Foreground");
builder.setContentText("I am a foreground service");
builder.setContentInfo("Content Info");
builder.setWhen(System.currentTimeMillis());
Intent activityIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
Notification notification = builder.build();
startForeground(FOREGROUND_ID, notification);
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onDestroy() {
Log.i(TAG, "WhiteService->onDestroy");
super.onDestroy();
}
}
「Agora Home AI」可以帮助用户实现可穿戴设备、智能家具设备、视频监控设备接入何控制。包括智能灯光、智能门窗、智能门锁、智能安防、智能手环监测、智能家电控制等配套产品,让用户实现多种品牌的智能设备在统一的交互平台内互联互通、统一管理、智能联动。为给用户创造更舒适、更安全、更节能的家居生活环境。
二等奖:Agora FIow
获得第二名的作品「Agora Flow」是一个基于声网+环信 SDK 搭建的音视频 Low Code Web 共享编辑器。
/// Load animation by name from the default bundle, Images are also loaded from the bundle + (nonnull instancetype)animationNamed:(nonnull NSString *)animationName NS_SWIFT_NAME(init(name:));
/// Loads animation by name from specified bundle, Images are also loaded from the bundle + (nonnull instancetype)animationNamed:(nonnull NSString *)animationName inBundle:(nonnull NSBundle *)bundle NS_SWIFT_NAME(init(name:bundle:));
/// Creates an animation from the deserialized JSON Dictionary + (nonnull instancetype)animationFromJSON:(nonnull NSDictionary *)animationJSON NS_SWIFT_NAME(init(json:));
/// Loads an animation from a specific file path. WARNING Do not use a web URL for file path. + (nonnull instancetype)animationWithFilePath:(nonnull NSString *)filePath NS_SWIFT_NAME(init(filePath:));
/// Creates an animation from the deserialized JSON Dictionary, images are loaded from the specified bundle + (nonnull instancetype)animationFromJSON:(nullable NSDictionary *)animationJSON inBundle:(nullable NSBundle *)bundle NS_SWIFT_NAME(init(json:bundle:));
/// Creates an animation from the LOTComposition, images are loaded from the specified bundle - (nonnull instancetype)initWithModel:(nullable LOTComposition *)model inBundle:(nullable NSBundle *)bundle;
/// Loads animation asynchrounously from the specified URL - (nonnull instancetype)initWithContentsOfURL:(nonnull NSURL *)url;
LOTAnimationView的属性
/// Flag is YES when the animation is playing @property (nonatomic, readonly) BOOL isAnimationPlaying;
/// Tells the animation to loop indefinitely. @property (nonatomic, assign) BOOL loopAnimation;
/// The animation will play forward and then backwards if loopAnimation is also YES @property (nonatomic, assign) BOOL autoReverseAnimation;
/// Sets a progress from 0 - 1 of the animation. If the animation is playing it will stop and the compeltion block will be called. /// The current progress of the animation in absolute time. /// e.g. a value of 0.75 always represents the same point in the animation, regardless of positive /// or negative speed. @property (nonatomic, assign) CGFloat animationProgress;
/// Sets the speed of the animation. Accepts a negative value for reversing animation. @property (nonatomic, assign) CGFloat animationSpeed;
/// Read only of the duration in seconds of the animation at speed of 1 @property (nonatomic, readonly) CGFloat animationDuration;
/// Enables or disables caching of the backing animation model. Defaults to YES @property (nonatomic, assign) BOOL cacheEnable;
/// Sets a completion block to call when the animation has completed @property (nonatomic, copy, nullable) LOTAnimationCompletionBlock completionBlock;
/// Set the amimation data @property (nonatomic, strong, nullable) LOTComposition *sceneModel;
在讲Metal的初级使用之前,我们先来看看苹果爸爸给我们的建议,首先,苹果建议我们Separate Your Rendering Loop,即分离我们渲染,Metal给我们提供了一个View,叫MTKView,它继承自UiView,它主要的渲染是通过MTKViewDelegate协议回调实现,两个重要的协议方法是:
1)当MTKView视图发生大小改变时调用
/*!
@method mtkView:drawableSizeWillChange:
@abstract Called whenever the drawableSize of the view will change
@discussion Delegate can recompute view and projection matricies or regenerate any buffers to be compatible with the new view size or resolution
//被stage_in 修饰的结构体的成员不能是如下这些.Packed vectors 紧密填充类型向量,matrices 矩阵,structs 结构体,references or pointers to type 某类型的引用或指针. arrays,vectors,matrices 标量,向量,矩阵数组.
public boolean dispatchTouchEvent(MotionEvent event) {//或 onTouchEvent
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
parent.requestDisallowInterceptTouchEvent(true);//不许拦截
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (父容器需要此类点击事件) {
parent.requestDisallowInterceptTouchEvent(false);//申请拦截
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
}
return super.dispatchTouchEvent(event);
}
/**
* Created by Varmin
* on 2017/7/5 16:16.
* 文件描述:left,content,right三个tag,在布局中给每个部分设置该tag。用于该ViewGroup内部给子View排序。
* 功能:默认全部关闭左右滑动。分别设置打开
*/
public class SlideView extends ViewGroup implements View.OnClickListener, View.OnLongClickListener {
private static final String TAG = "SlideView";
public final String LEFT = "left";
public final String CONTENT = "content";
public final String RIGHT = "right";
private Scroller mScroller;
/**
* scroller滑动时间。默认250ms
*/
public static final int DEFAULT_TIMEOUT = 250;
public static final int SLOW_TIMEOUT = 500;
/**
* 左右View的宽度
*/
private int leftWidth;
private int rightWidth;
private GestureDetector mGesture;
private ViewConfiguration mViewConfig;
public SlideView(Context context) {
super(context);
init(context);
}
public SlideView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SlideView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mScroller = new Scroller(context);
//都是自己处理的,这里没有用到该手势方法
//缺点:误差有些大。这种精确滑动的,最好自己判断
mGesture = new GestureDetector(context, new SlideGestureDetector());
mViewConfig = ViewConfiguration.get(context);
//默认false
setClickable(true);
}
publicclassPointEvaluatorimplementsTypeEvaluator<Point> { @Override public Point evaluate(float fraction, Point startValue, Point endValue) { int radius = (int) (startValue.getRadius() +
fraction*(endValue.getRadius() - startValue.getRadius())); returnnew Point(radius);
}
}
源码:
public class Parent<T> {
public void func(T t){
}
}
public class Child<T extends Number> extends Parent<T> {
public T get() {
return null;
}
public void func(T t){
}
}
void test(){
Child<Integer> child = new Child<>();
Integer i = child.get();
}
---------------------------------------------------------
字节码:
public class Parent {
public void func(Object t){
}
}
public class Child extends Parent {
public Number get() {
return null;
}
public void func(Number t) {
}
桥方法 - synthetic
public void func(Object t){
func((Number)t);
}
}
void test() {
Child<Integer> child = new Child();
// 插入强制类型转换
Integer i = (Integer) child.get();
}
步骤1:Parent 中的类型参数 T 被擦除为 Object,而 Child 中的类型参数 T 被擦除为 Number;
Java:
<T> List<T> filter(List list) {
List<T> result = new ArrayList<>();
for (Object e : list) {
if (e instanceof T) { // compiler error
result.add(e);
}
}
return result;
}
---------------------------------------------------
Kotlin:
fun <T> filter(list: List<*>): List<T> {
val result = ArrayList<T>()
for (e in list) {
if (e is T) { // cannot check for instance of erased type: T
result.add(e)
}
}
return result
}
在Kotlin中,有一种方法可以突破这种限制,即:带实化类型参数的内联函数:
Kotlin:
inline fun <reified T> filter(list: List<*>): List<T> {
val result = ArrayList<T>()
for (e in list) {
if (e is T) {
result.add(e)
}
}
return result
}
关键在于inline和reified,这两者的语义是:
inline(内联函数): Kotlin编译器将内联函数的字节码插入到每一次调用方法的地方
reified(实化类型参数): 在插入的字节码中,使用类型实参的确切类型代替类型实参
规则很好理解,对吧。很明显,当发生方法内联时,方法体字节码就变成了:
调用:
val list = listOf("", 1, false)
val strList = filter<String>(list)
---------------------------------------------------
内联后:
val result = ArrayList<String>()
for (e in list) {
if (e is String) {
result.add(e)
}
}
// Collections.java public static void copy(List<? super T> dest, List<? extends T> src) { }
在 Kotlin 中,变型写法会有些不同,但是语义是完全一样的:
协变:
val l0: MutableList<*> 相当于MutableList<out Any?>
val l1: MutableList<out Number>
val l2 = ArrayList<Int>()
l0 = l2 // OK
l1 = l2 // OK
---------------------------------------------------
逆变:
val l1: MutableList<in Int>
val l2 = ArrayList<Number>()
l1 = l2 // OK
functionMyRunnable(){ this.run = function(){
}
} functionRunnable(){ this.run = function(){
}
} var ss = new MyRunnable();
ss.run(); // 只要对象有相同方法签名的方法即可
ss = new Runnable();
ss.run();
不同点:静态代理存在重复性和脆弱性的缺点;而动态代理(搭配泛型参数)可以实现了一个代理同时处理 N 种基础接口,一定程度上规避了静态代理的缺点。从原理上讲,静态代理的代理类 Class 文件在编译期生成,而动态代理的代理类 Class 文件在运行时生成,代理类在 coding 阶段并不存在,代理关系直到运行时才确定。
iCarousel 是一个旨在简化 iPhone、iPad 和 Mac OS 上各种类型的轮播(分页、滚动视图)的实现的类。iCarousel 实现了许多常见的效果,例如圆柱形、平面和“CoverFlow”风格的轮播,并提供钩子来实现您自己的定制效果。与许多其他“CoverFlow”库不同,iCarousel 可以处理任何类型的视图,而不仅仅是图像,因此它非常适合在您的应用程序中以流畅且令人印象深刻的方式呈现分页数据。它还使得以最少的代码更改在不同的轮播效果之间切换变得非常容易。
支持的操作系统和 SDK 版本
支持的构建目标 - iOS 10.0 / Mac OS 10.12(Xcode 8.0,Apple LLVM 编译器 8.0)
注意:iCarouselTypeCoverFlow和iCarouselTypeCoverFlow2类型之间的区别非常微妙,但是 for 的逻辑要iCarouselTypeCoverFlow2复杂得多。如果您轻弹转盘,它们基本上是相同的,但是如果您用手指缓慢拖动转盘,则差异应该很明显。iCarouselTypeCoverFlow2旨在尽可能接近地模拟标准 Apple CoverFlow 效果,并且将来可能会为了该目标而进行微妙的更改。
显示类型可视化示例
线性
旋转式
倒转
圆筒
倒置气缸
Cover Flow功能
特性
iCarousel 具有以下属性(注意:对于 Mac OS,在使用属性时将 NSView 替换为 UIView):
- (void)changeColor:(id <DKNightVersionChangeColorProtocol>)object { if ([object respondsToSelector:@selector(changeColor)]) { [object changeColor]; } if ([object respondsToSelector:@selector(subviews)]) { if (![object subviews]) { // Basic case, do nothing. return; } else { for (id subview in [object subviews]) { // recursive darken all the subviews of current view. [self changeColor:subview]; if ([subview respondsToSelector:@selector(changeColor)]) { [subview changeColor]; } } } } }
NORMAL NIGHT RED #ffffff #343434 #fafafa BG #aaaaaa #313131 #aaaaaa SEP #0000ff #ffffff #fa0000 TINT #000000 #ffffff #000000 TEXT #ffffff #444444 #ffffff BAR
// // <%= klass.name %>+Night.m // <%= klass.name %>+Night // // Copyright (c) 2015 Draveness. All rights reserved. // // These files are generated by ruby script, if you want to modify code // in this file, you are supposed to update the ruby code, run it and // test it. And finally open a pull request.
platform :ios, '7.0' target 'UnitTestDemoTests' do pod 'AFNetworking', '~> 2.5.0' pod 'STAlertView', '~> 1.0.0' end target 'UnitTestDemoTestsTests' do pod 'AFNetworking', '~> 2.5.0' pod 'STAlertView', '~> 1.0.0' end
Assuner$ cd Desktop/ Desktop Assuner$ cd ASUnitTestFirstDemo/ ASUnitTestFirstDemo Assuner$ xcodebuild test -project ASUnitTestFirstDemo.xcodeproj -scheme ASUnitTestFirstDemo -destination 'platform=iOS Simulator,OS=11.4,name=iPhone 7' // 可以有多个destination
结果
Test Suite 'All tests' started at 2017-09-11 11:12:16.348 Test Suite 'ASUnitTestFirstDemoTests.xctest' started at 2017-09-11 11:12:16.349 Test Suite 'ASUnitTestFirstDemoTests' started at 2017-09-11 11:12:16.349 Test Case '-[ASUnitTestFirstDemoTests testLevel1]' started. Test Case '-[ASUnitTestFirstDemoTests testLevel1]' passed (0.001 seconds). Test Case '-[ASUnitTestFirstDemoTests testLevel2]' started. /Users/liyongguang-eleme-iOS-Development/Desktop/ASUnitTestFirstDemo/ASUnitTestFirstDemoTests/ASUnitTestFirstDemoTests.m:46: error: -[ASUnitTestFirstDemoTests testLevel2] : ((tax) equal to (45.0)) failed: ("-60") is not equal to ("45") - 用例2测试失败 Test Case '-[ASUnitTestFirstDemoTests testLevel2]' failed (1.007 seconds). Test Suite 'ASUnitTestFirstDemoTests' failed at 2017-09-11 11:12:17.358. Executed 2 tests, with 1 failure (0 unexpected) in 1.008 (1.009) seconds Test Suite 'ASUnitTestFirstDemoTests.xctest' failed at 2017-09-11 11:12:17.359. Executed 2 tests, with 1 failure (0 unexpected) in 1.008 (1.010) seconds Test Suite 'All tests' failed at 2017-09-11 11:12:17.360. Executed 2 tests, with 1 failure (0 unexpected) in 1.008 (1.012) seconds Failing tests: -[ASUnitTestFirstDemoTests testLevel2] ** TEST FAILED **
如果是workspace
xcodebuild -workspace ASKiwiTest.xcworkspace -scheme ASKiwiTest-Example -destination 'platform=iOS Simulator,OS=11.0,name=iPhone 7' test
//thread A for (int i =0; i <100000; i ++) { if (i %2==0) { self.arr =@[@"1", @"2", @"3"];
}else { self.arr =@[@"1"];
} NSLog(@"Thread A: %@\n", self.arr);
}
//thread B if (self.arr.count >=2) {
NSString* str = [self.arr objectAtIndex:1];
}
就算在 thread B 中针对 arr 数组进行了大小判断,但是仍然可能在 objectAtIndex: 操作时被改变数组长度,导致出错。这种情况声明为 atomic 也没有用。
有:高优先级任务A / 次高优先级任务B / 低优先级任务C / 资源Z 。 A 等待 C 执行后的 Z, 而 B 并不需要 Z,抢先获得时间片执行。 C 由于没有时间片,无法执行(优先级相对没有B高)。 这种情况造成 A 在C 之后执行,C在B之后,间接的高优先级A在次高优先级任务B 之后执行, 使得优先级被倒置了。(假设: A 等待资源时不是阻塞等待,而是忙循环,则可能永远无法获得资源。此时 C 无法与 A 争夺 CPU 时间,从而 C 无法执行,进而无法释放资源。造成的后果,就是 A 无法获得 Z 而继续推进。)
而 OSSpinLock 忙等的机制,就可能造成高优先级一直 running ,占用 cpu 时间片。而低优先级任务无法抢占时间片,变成迟迟完不成,不释放锁的情况。
protectedClass loadClass(String name, boolean resolve) throwsClassNotFoundException
{ // First, check if the class has already been loaded Classc = findLoadedClass(name); if (c == null) { try { if (parent != null) { // 先从父类加载器中进行加载 c = parent.loadClass(name, false);
} else { c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader
}
if (c == null) { // 没有找到,再自己加载 c = findClass(name);
}
} returnc;
}
android.content.ActivityNotFoundException: Unable to find explicit activity class {com.zyg.commontec/com.zyg.plugin.PluginActivity}; have you declared this activity in your AndroidManifest.xml?
publicclassInstrumentation{ publicstaticvoidcheckStartActivityResult(int res, Object intent){ if (!ActivityManager.isStartResultFatalError(res)) { return;
}
switch (res) { case ActivityManager.START_INTENT_NOT_RESOLVED: case ActivityManager.START_CLASS_NOT_FOUND: if (intent instanceof Intent && ((Intent)intent).getComponent() != null) thrownew ActivityNotFoundException( "Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?"); thrownew ActivityNotFoundException( "No Activity found to handle " + intent);
...
}
}
}
val file = JarFile(jarFile) var enumeration = file.entries() while (enumeration.hasMoreElements()) { val jarEntry = enumeration.nextElement() if (jarEntry.name.endsWith("XXRouterTable.class")) { val inputStream = file.getInputStream(jarEntry) val classReader = ClassReader(inputStream) if (Arrays.toString(classReader.interfaces)
.contains("IHTRouterTBCollect")
) {
tableList.add(
Pair(
classReader.className,
dest?.absolutePath
)
)
}
inputStream.close()
} elseif (jarEntry.name.endsWith("HTRouterInitializer.class")) {
registerInitClass = dest
}
}
file.close()
}
对目标Class注入路由表初始化代码
funasmInsertMethod(originFile: File?) {
val optJar = File(originFile?.parent, originFile?.name + ".opt") if (optJar.exists())
optJar.delete() val jarFile = JarFile(originFile) val enumeration = jarFile.entries() val jarOutputStream = JarOutputStream(FileOutputStream(optJar))
while (enumeration.hasMoreElements()) { val jarEntry = enumeration.nextElement() val entryName = jarEntry.getName() val zipEntry = ZipEntry(entryName) val inputStream = jarFile.getInputStream(jarEntry) //插桩class if (entryName.endsWith("RouterInitializer.class")) { //class文件处理
jarOutputStream.putNextEntry(zipEntry) val classReader = ClassReader(IOUtils.toByteArray(inputStream)) val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS) val cv = RegisterClassVisitor(Opcodes.ASM5, classWriter,tableList)
classReader.accept(cv, EXPAND_FRAMES) val code = classWriter.toByteArray()
jarOutputStream.write(code)
} else {
jarOutputStream.putNextEntry(zipEntry)
jarOutputStream.write(IOUtils.toByteArray(inputStream))
}
jarOutputStream.closeEntry()
} //结束
jarOutputStream.close()
jarFile.close() if (originFile?.exists() == true) {
Files.delete(originFile.toPath())
}
optJar.renameTo(originFile)
}
(lldb) vs 0x7fe73550a090 Use the following and (q) to quit. (w) move to superview //移动到父视图 (s) move to first subview //移动到第一个子视图 (a) move to previous sibling //移动上一个兄弟视图 (d) move to next sibling //移动下一个兄弟视图 (p) print the hierarchy //打印视图层级结构
publicstaticvoid initAttr(String preFix, File file) {
HashMap format = format(preFix, file); String className = format.get("className"); String result = format.get("result");
StringBuilder sb = new StringBuilder();
sb.append("TypedArray a = context.obtainStyledAttributes(attrs, R.styleable." + className + ");\r\n");
format.forEach((s, s2) -> { String styleableName = className + "_" + preFix + s; if (s.contains("_")) { String[] partStrArray = s.split("_");
s = ""; for (String part : partStrArray) { String partStr = upAChar(part);
s += partStr;
}
} if (s2.equals("dimension")) { // mPbBgHeight = (int) a.getDimension(R.styleable.TolyProgressBar_z_pb_bg_height, mPbBgHeight);
sb.append("m" + s + " = (int) a.getDimension(R.styleable." + styleableName + ", m" + s + ");\r\n");
} if (s2.equals("color")) { // mPbTxtColor = a.getColor(R.styleable.TolyProgressBar_z_pb_txt_color, mPbTxtColor);
sb.append("m" + s + " = a.getColor(R.styleable." + styleableName + ", m" + s + ");\r\n");
} if (s2.equals("boolean")) { // mPbTxtColor = a.getColor(R.styleable.TolyProgressBar_z_pb_txt_color, mPbTxtColor);
sb.append("m" + s + " = a.getBoolean(R.styleable." + styleableName + ", m" + s + ");\r\n");
} if (s2.equals("string")) { // mPbTxtColor = a.getColor(R.styleable.TolyProgressBar_z_pb_txt_color, mPbTxtColor);
sb.append("m" + s + " = a.getString(R.styleable." + styleableName + ");\r\n");
}
});
sb.append("a.recycle();\r\n");
System.out.println(result);
System.out.println(sb.toString());
}
/**
* 读取文件+解析
*
* @param preFix 前缀
* @param file 文件路径
*/ publicstatic HashMap format(String preFix, File file) {
HashMap container = new HashMap<>(); if (!file.exists() && file.isDirectory()) { returnnull;
}
FileReader fr = null; try {
fr = new FileReader(file); //字符数组循环读取
char[] buf = new char[1024];
int len = 0;
StringBuilder sb = new StringBuilder(); while ((len = fr.read(buf)) != -1) {
sb.append(newString(buf, 0, len));
} String className = sb.toString().split(""));
container.put("className", className); String[] split = sb.toString().split("<"); String part1 = "private"; Stringtype = "";//类型 String name = ""; String result = ""; String def = "";//默认值
StringBuilder sb2 = new StringBuilder(); for (String s : split) { if (s.contains(preFix)) {
result = s.split(preFix)[1];
name = result.substring(0, result.indexOf("\"")); type = result.split("format=\"")[1]; type = type.substring(0, type.indexOf("\""));
container.put(name, type); if (type.contains("color") || type.contains("dimension") || type.contains("integer")) { type = "int";
def = "0";
} if (result.contains("fraction")) { type = "float";
def = "0.f";
} if (result.contains("string")) { type = "String";
def = "\"toly\"";
} if (result.contains("boolean")) { type = "boolean";
def = "false";
} if (name.contains("_")) { String[] partStrArray = name.split("_");
name = ""; for (String part : partStrArray) { String partStr = upAChar(part);
name += partStr;
}
sb2.append(part1 + " " + type + " m" + name + "= " + def + ";\r\n");
}
container.put("result", sb2.toString());
}
}
} catch (Exception e) {
e.printStackTrace();
} finally { try { if (fr != null) {
fr.close();
}
} catch (Exception e) {
e.printStackTrace();
}
} return container;
}