Xcode – Error ITMS-90168: “The binary you uploaded was invalid.”

今天发布产品,运维人员在用Application Loader提交ipa时,遇到了:

Error ITMS-90168: “The binary you uploaded was invalid.”
The resulting API analysis file is too large. We were unable to validate your API usage prior to delivery. 
This is just an informational message.

网上搜了一下,大部分是指向:
http://stackoverflow.com/questions/32908964/error-itms-90168-the-binary-you-uploaded-was-invalid

然而按照这个操作并没有用,实际上我猜测是打包使用的签名,并不是发布用的签名文件。
也有说必须使用最新版Application Loader就好使的,然而也并没有什么卵用,不过提示不一样了,还是上面的错误码,但是提示签名缺失或者不匹配发布的签名。
于是检查编译脚本,发现是前几天证书过期了,之后使用的是新证书,但是编译脚本里并没有更正过来。。。
更改使用证书文件,果断重新编译,使用最新版本的Application Loader(3.6)提交,通过验证!

另外还有个小问题,我们是发布两个产品,另一个仅仅显示了:

Error ITMS-90168: “The binary you uploaded was invalid.”

检查cert也是匹配的,百思不得其解后,选择重新编译,居然也通过了验证。。。
看来rebuild真的包治百病。。。

最后关于这个问题,我也搜索了很多案例,总结一下经验:
1、使用最新版Xcode自带的Application Loader
2、图标是否缺失
3、签名文件是否匹配
4、rebuild
At last,wish good luck with you.

关于本站建立的初衷

首先谈谈本站域名“qdota.com”,因为本人是资深dota玩家,这样的域名简单也好记😁,以后也许会发一些dota的文章,写个dota app展示一下。
因为建站没经验,只好先用着模版wordpress,基本上就是blog了,可以写写工作中遇到的问题,很多时候搜关键字不好使,这样写的文章应该会被搜录的几率高一点。
然后引入了站点统计,展望一下未来广告收入💰💰💰。。。

任重道远,坚持不懈!争取每天抽事件写点什么,希望几年后,今天的播种会有惊喜。

Android[控件]NumberPicker使用问题汇总

NumberPicker的核心函数:
1、setDisplayedValues

    /**
     * Sets the values to be displayed.
     *
     * @param displayedValues The displayed values.
     *
     * <strong>Note:</strong> The length of the displayed values array
     * must be equal to the range of selectable numbers which is equal to
     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
     */
    public void setDisplayedValues(String[] displayedValues) {
        if (mDisplayedValues == displayedValues) {
            return;
        }
        mDisplayedValues = displayedValues;
        if (mDisplayedValues != null) {
            // Allow text entry rather than strictly numeric entry.
            mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
                    | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
        } else {
            mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
        }
        updateInputTextView();
        initializeSelectorWheelIndices();
        tryComputeMaxWidth();
    }

2、setMaxValue

    /**
     * Sets the max value of the picker.
     *
     * @param maxValue The max value inclusive.
     *
     * <strong>Note:</strong> The length of the displayed values array
     * set via {@link #setDisplayedValues(String[])} must be equal to the
     * range of selectable numbers which is equal to
     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
     */
    public void setMaxValue(int maxValue) {
        if (mMaxValue == maxValue) {
            return;
        }
        if (maxValue < 0) {
            throw new IllegalArgumentException("maxValue must be >= 0");
        }
        mMaxValue = maxValue;
        if (mMaxValue < mValue) {
            mValue = mMaxValue;
        }
        updateWrapSelectorWheel();
        initializeSelectorWheelIndices();
        updateInputTextView();
        tryComputeMaxWidth();
        invalidate();
    }

3、setMinValue

    /**
     * Sets the min value of the picker.
     *
     * @param minValue The min value inclusive.
     *
     * <strong>Note:</strong> The length of the displayed values array
     * set via {@link #setDisplayedValues(String[])} must be equal to the
     * range of selectable numbers which is equal to
     * {@link #getMaxValue()} - {@link #getMinValue()} + 1.
     */
    public void setMinValue(int minValue) {
        if (mMinValue == minValue) {
            return;
        }
        if (minValue < 0) {
            throw new IllegalArgumentException("minValue must be >= 0");
        }
        mMinValue = minValue;
        if (mMinValue > mValue) {
            mValue = mMinValue;
        }
        updateWrapSelectorWheel();
        initializeSelectorWheelIndices();
        updateInputTextView();
        tryComputeMaxWidth();
        invalidate();
    }

4、setValue

    /**
     * Set the current value for the number picker.
     * <p>
     * If the argument is less than the {@link NumberPicker#getMinValue()} and
     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
     * current value is set to the {@link NumberPicker#getMinValue()} value.
     * </p>
     * <p>
     * If the argument is less than the {@link NumberPicker#getMinValue()} and
     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
     * current value is set to the {@link NumberPicker#getMaxValue()} value.
     * </p>
     * <p>
     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
     * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the
     * current value is set to the {@link NumberPicker#getMaxValue()} value.
     * </p>
     * <p>
     * If the argument is less than the {@link NumberPicker#getMaxValue()} and
     * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the
     * current value is set to the {@link NumberPicker#getMinValue()} value.
     * </p>
     *
     * @param value The current value.
     * @see #setWrapSelectorWheel(boolean)
     * @see #setMinValue(int)
     * @see #setMaxValue(int)
     */
    public void setValue(int value) {
        setValueInternal(value, false);
    }
 
    /**
     * Sets the current value of this NumberPicker.
     *
     * @param current The new value of the NumberPicker.
     * @param notifyChange Whether to notify if the current value changed.
     */
    private void setValueInternal(int current, boolean notifyChange) {
        if (mValue == current) {
            return;
        }
        // Wrap around the values if we go past the start or end
        if (mWrapSelectorWheel) {
            current = getWrappedSelectorIndex(current);
        } else {
            current = Math.max(current, mMinValue);
            current = Math.min(current, mMaxValue);
        }
        int previous = mValue;
        mValue = current;
        updateInputTextView();
        if (notifyChange) {
            notifyChange(previous, current);
        }
        initializeSelectorWheelIndices();
        invalidate();
    }

5、setFormatter

    /**
     * Set the formatter to be used for formatting the current value.
     * <p>
     * Note: If you have provided alternative values for the values this
     * formatter is never invoked.
     * </p>
     *
     * @param formatter The formatter object. If formatter is <code>null</code>,
     *            {@link String#valueOf(int)} will be used.
     *@see #setDisplayedValues(String[])
     */
    public void setFormatter(Formatter formatter) {
        if (formatter == mFormatter) {
            return;
        }
        mFormatter = formatter;
        initializeSelectorWheelIndices();
        updateInputTextView();
    }

仔细观察实现函数,发现它们都会触发一个内部函数updateInputTextView




    /**
     * Updates the view of this NumberPicker. If displayValues were specified in
     * the string corresponding to the index specified by the current value will
     * be returned. Otherwise, the formatter specified in {@link #setFormatter}
     * will be used to format the number.
     *
     * @return Whether the text was updated.
     */
    private boolean updateInputTextView() {
        /*
         * If we don't have displayed values then use the current number else
         * find the correct value in the displayed values for the current
         * number.
         */
        String text = (mDisplayedValues == null) ? formatNumber(mValue)
                : mDisplayedValues[mValue - mMinValue];
        if (!TextUtils.isEmpty(text) && !text.equals(mInputText.getText().toString())) {
            mInputText.setText(text);
            return true;
        }
 
        return false;
    }

这里如果mDisplayedValues不为空时,显示索引为mValue – mMinValue的值!!!
看到这里,大家心里应该有警惕了,如果mDisplayedValues的长度变短了……而索引还未改变……索引就会越界……
那么明显的,改变显示的数据集,长度发生变化的,上面的一系列函数调用顺序就需要好好思量了
a)当displayedValues长度变大时
i、setDisplayedValues
ii、setMaxValue
iii、setMinValue
iV、setValue
b)否则
i、setMaxValue
ii、setMinValue
iii、setDisplayedValues
iV、setValue
a情况,先扩张displayedValues容量,索引仍然保持不变
b情况,先缩小maxValue的值,同时会限制value也缩小,同步的索引也变小,最后赋值displayedValues

文章本应到此为止,可是实际上,在setValue之后,NumberPicker中间的文本并没有显示对应的值,原本以为
添上invalidate就可以了,结果是不行;在界面点击中间的文本后,文字会刷新显示正确。
解决办法如下代码:




    /**
     * NumberPicker EditText
     */
    public static void applyNumberPickerEditTextStyle(NumberPicker picker, String text) {
        EditText editText = null;
        for (int i = 0; i < picker.getChildCount(); i++) {
            View child = picker.getChildAt(i);
            if (child instanceof EditText) {
                editText = (EditText) child;
                break;
            }
        }
        if (editText != null) {
            // disable text input
            editText.setFocusable(false);
            editText.setFilters(new InputFilter[0]);
            editText.setText(text);
        }
    }